source: branches/3.2/mindi-busybox/procps/top.c

Last change on this file was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
File size: 33.9 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 * Sept 2008: Vineet Gupta <vineet.gupta@arc.com>
21 * Added Support for reporting SMP Information
22 * - CPU where process was last seen running
23 *   (to see effect of sched_setaffinity() etc)
24 * - CPU time split (idle/IO/wait etc) per CPU
25 *
26 * Copyright (c) 1992 Branko Lankester
27 * Copyright (c) 1992 Roger Binns
28 * Copyright (C) 1994-1996 Charles L. Blake.
29 * Copyright (C) 1992-1998 Michael K. Johnson
30 *
31 * Licensed under GPLv2, see file LICENSE in this source tree.
32 */
33/* How to snapshot /proc for debugging top problems:
34 * for f in /proc/[0-9]*""/stat; do
35 *         n=${f#/proc/}
36 *         n=${n%/stat}_stat
37 *         cp $f $n
38 * done
39 * cp /proc/stat /proc/meminfo /proc/loadavg .
40 * top -bn1 >top.out
41 *
42 * ...and how to run top on it on another machine:
43 * rm -rf proc; mkdir proc
44 * for f in [0-9]*_stat; do
45 *         p=${f%_stat}
46 *         mkdir -p proc/$p
47 *         cp $f proc/$p/stat
48 * done
49 * cp stat meminfo loadavg proc
50 * chroot . ./top -bn1 >top1.out
51 */
52
53//config:config TOP
54//config:   bool "top"
55//config:   default y
56//config:   help
57//config:     The top program provides a dynamic real-time view of a running
58//config:     system.
59//config:
60//config:config FEATURE_TOP_CPU_USAGE_PERCENTAGE
61//config:   bool "Show CPU per-process usage percentage"
62//config:   default y
63//config:   depends on TOP
64//config:   help
65//config:     Make top display CPU usage for each process.
66//config:     This adds about 2k.
67//config:
68//config:config FEATURE_TOP_CPU_GLOBAL_PERCENTS
69//config:   bool "Show CPU global usage percentage"
70//config:   default y
71//config:   depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
72//config:   help
73//config:     Makes top display "CPU: NN% usr NN% sys..." line.
74//config:     This adds about 0.5k.
75//config:
76//config:config FEATURE_TOP_SMP_CPU
77//config:   bool "SMP CPU usage display ('c' key)"
78//config:   default y
79//config:   depends on FEATURE_TOP_CPU_GLOBAL_PERCENTS
80//config:   help
81//config:     Allow 'c' key to switch between individual/cumulative CPU stats
82//config:     This adds about 0.5k.
83//config:
84//config:config FEATURE_TOP_DECIMALS
85//config:   bool "Show 1/10th of a percent in CPU/mem statistics"
86//config:   default y
87//config:   depends on FEATURE_TOP_CPU_USAGE_PERCENTAGE
88//config:   help
89//config:     Show 1/10th of a percent in CPU/mem statistics.
90//config:     This adds about 0.3k.
91//config:
92//config:config FEATURE_TOP_SMP_PROCESS
93//config:   bool "Show CPU process runs on ('j' field)"
94//config:   default y
95//config:   depends on TOP
96//config:   help
97//config:     Show CPU where process was last found running on.
98//config:     This is the 'j' field.
99//config:
100//config:config FEATURE_TOPMEM
101//config:   bool "Topmem command ('s' key)"
102//config:   default y
103//config:   depends on TOP
104//config:   help
105//config:     Enable 's' in top (gives lots of memory info).
106
107#include "libbb.h"
108
109
110typedef struct top_status_t {
111    unsigned long vsz;
112#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
113    unsigned long ticks;
114    unsigned pcpu; /* delta of ticks */
115#endif
116    unsigned pid, ppid;
117    unsigned uid;
118    char state[4];
119    char comm[COMM_LEN];
120#if ENABLE_FEATURE_TOP_SMP_PROCESS
121    int last_seen_on_cpu;
122#endif
123} top_status_t;
124
125typedef struct jiffy_counts_t {
126    /* Linux 2.4.x has only first four */
127    unsigned long long usr, nic, sys, idle;
128    unsigned long long iowait, irq, softirq, steal;
129    unsigned long long total;
130    unsigned long long busy;
131} jiffy_counts_t;
132
133/* This structure stores some critical information from one frame to
134   the next. Used for finding deltas. */
135typedef struct save_hist {
136    unsigned long ticks;
137    pid_t pid;
138} save_hist;
139
140typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
141
142
143enum { SORT_DEPTH = 3 };
144
145
146struct globals {
147    top_status_t *top;
148    int ntop;
149    smallint inverted;
150#if ENABLE_FEATURE_TOPMEM
151    smallint sort_field;
152#endif
153#if ENABLE_FEATURE_TOP_SMP_CPU
154    smallint smp_cpu_info; /* one/many cpu info lines? */
155#endif
156    unsigned lines;  /* screen height */
157#if ENABLE_FEATURE_USE_TERMIOS
158    struct termios initial_settings;
159    int scroll_ofs;
160#define G_scroll_ofs G.scroll_ofs
161#else
162#define G_scroll_ofs 0
163#endif
164#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
165    cmp_funcp sort_function[1];
166#else
167    cmp_funcp sort_function[SORT_DEPTH];
168    struct save_hist *prev_hist;
169    int prev_hist_count;
170    jiffy_counts_t cur_jif, prev_jif;
171    /* int hist_iterations; */
172    unsigned total_pcpu;
173    /* unsigned long total_vsz; */
174#endif
175#if ENABLE_FEATURE_TOP_SMP_CPU
176    /* Per CPU samples: current and last */
177    jiffy_counts_t *cpu_jif, *cpu_prev_jif;
178    int num_cpus;
179#endif
180#if ENABLE_FEATURE_USE_TERMIOS
181    char kbd_input[KEYCODE_BUFFER_SIZE];
182#endif
183    char line_buf[80];
184}; //FIX_ALIASING; - large code growth
185enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
186#define G (*(struct globals*)&bb_common_bufsiz1)
187struct BUG_bad_size {
188    char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
189    char BUG_line_buf_too_small[LINE_BUF_SIZE > 80 ? 1 : -1];
190};
191#define top              (G.top               )
192#define ntop             (G.ntop              )
193#define sort_field       (G.sort_field        )
194#define inverted         (G.inverted          )
195#define smp_cpu_info     (G.smp_cpu_info      )
196#define initial_settings (G.initial_settings  )
197#define sort_function    (G.sort_function     )
198#define prev_hist        (G.prev_hist         )
199#define prev_hist_count  (G.prev_hist_count   )
200#define cur_jif          (G.cur_jif           )
201#define prev_jif         (G.prev_jif          )
202#define cpu_jif          (G.cpu_jif           )
203#define cpu_prev_jif     (G.cpu_prev_jif      )
204#define num_cpus         (G.num_cpus          )
205#define total_pcpu       (G.total_pcpu        )
206#define line_buf         (G.line_buf          )
207#define INIT_G() do { } while (0)
208
209enum {
210    OPT_d = (1 << 0),
211    OPT_n = (1 << 1),
212    OPT_b = (1 << 2),
213    OPT_m = (1 << 3),
214    OPT_EOF = (1 << 4), /* pseudo: "we saw EOF in stdin" */
215};
216#define OPT_BATCH_MODE (option_mask32 & OPT_b)
217
218
219#if ENABLE_FEATURE_USE_TERMIOS
220static int pid_sort(top_status_t *P, top_status_t *Q)
221{
222    /* Buggy wrt pids with high bit set */
223    /* (linux pids are in [1..2^15-1]) */
224    return (Q->pid - P->pid);
225}
226#endif
227
228static int mem_sort(top_status_t *P, top_status_t *Q)
229{
230    /* We want to avoid unsigned->signed and truncation errors */
231    if (Q->vsz < P->vsz) return -1;
232    return Q->vsz != P->vsz; /* 0 if ==, 1 if > */
233}
234
235
236#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
237
238static int pcpu_sort(top_status_t *P, top_status_t *Q)
239{
240    /* Buggy wrt ticks with high bit set */
241    /* Affects only processes for which ticks overflow */
242    return (int)Q->pcpu - (int)P->pcpu;
243}
244
245static int time_sort(top_status_t *P, top_status_t *Q)
246{
247    /* We want to avoid unsigned->signed and truncation errors */
248    if (Q->ticks < P->ticks) return -1;
249    return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
250}
251
252static int mult_lvl_cmp(void* a, void* b)
253{
254    int i, cmp_val;
255
256    for (i = 0; i < SORT_DEPTH; i++) {
257        cmp_val = (*sort_function[i])(a, b);
258        if (cmp_val != 0)
259            break;
260    }
261    return inverted ? -cmp_val : cmp_val;
262}
263
264static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
265{
266#if !ENABLE_FEATURE_TOP_SMP_CPU
267    static const char fmt[] = "cpu %llu %llu %llu %llu %llu %llu %llu %llu";
268#else
269    static const char fmt[] = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu";
270#endif
271    int ret;
272
273    if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */)
274        return 0;
275    ret = sscanf(line_buf, fmt,
276            &p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
277            &p_jif->iowait, &p_jif->irq, &p_jif->softirq,
278            &p_jif->steal);
279    if (ret >= 4) {
280        p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle
281            + p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal;
282        /* procps 2.x does not count iowait as busy time */
283        p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait;
284    }
285
286    return ret;
287}
288
289static void get_jiffy_counts(void)
290{
291    FILE* fp = xfopen_for_read("stat");
292
293    /* We need to parse cumulative counts even if SMP CPU display is on,
294     * they are used to calculate per process CPU% */
295    prev_jif = cur_jif;
296    if (read_cpu_jiffy(fp, &cur_jif) < 4)
297        bb_error_msg_and_die("can't read /proc/stat");
298
299#if !ENABLE_FEATURE_TOP_SMP_CPU
300    fclose(fp);
301    return;
302#else
303    if (!smp_cpu_info) {
304        fclose(fp);
305        return;
306    }
307
308    if (!num_cpus) {
309        /* First time here. How many CPUs?
310         * There will be at least 1 /proc/stat line with cpu%d
311         */
312        while (1) {
313            cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus);
314            if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4)
315                break;
316            num_cpus++;
317        }
318        if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */
319            smp_cpu_info = 0;
320
321        cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus);
322
323        /* Otherwise the first per cpu display shows all 100% idles */
324        usleep(50000);
325    } else { /* Non first time invocation */
326        jiffy_counts_t *tmp;
327        int i;
328
329        /* First switch the sample pointers: no need to copy */
330        tmp = cpu_prev_jif;
331        cpu_prev_jif = cpu_jif;
332        cpu_jif = tmp;
333
334        /* Get the new samples */
335        for (i = 0; i < num_cpus; i++)
336            read_cpu_jiffy(fp, &cpu_jif[i]);
337    }
338#endif
339    fclose(fp);
340}
341
342static void do_stats(void)
343{
344    top_status_t *cur;
345    pid_t pid;
346    int i, last_i, n;
347    struct save_hist *new_hist;
348
349    get_jiffy_counts();
350    total_pcpu = 0;
351    /* total_vsz = 0; */
352    new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
353    /*
354     * Make a pass through the data to get stats.
355     */
356    /* hist_iterations = 0; */
357    i = 0;
358    for (n = 0; n < ntop; n++) {
359        cur = top + n;
360
361        /*
362         * Calculate time in cur process.  Time is sum of user time
363         * and system time
364         */
365        pid = cur->pid;
366        new_hist[n].ticks = cur->ticks;
367        new_hist[n].pid = pid;
368
369        /* find matching entry from previous pass */
370        cur->pcpu = 0;
371        /* do not start at index 0, continue at last used one
372         * (brought hist_iterations from ~14000 down to 172) */
373        last_i = i;
374        if (prev_hist_count) do {
375            if (prev_hist[i].pid == pid) {
376                cur->pcpu = cur->ticks - prev_hist[i].ticks;
377                total_pcpu += cur->pcpu;
378                break;
379            }
380            i = (i+1) % prev_hist_count;
381            /* hist_iterations++; */
382        } while (i != last_i);
383        /* total_vsz += cur->vsz; */
384    }
385
386    /*
387     * Save cur frame's information.
388     */
389    free(prev_hist);
390    prev_hist = new_hist;
391    prev_hist_count = ntop;
392}
393
394#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
395
396#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
397/* formats 7 char string (8 with terminating NUL) */
398static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
399{
400    unsigned t;
401    if (value >= total) { /* 100% ? */
402        strcpy(pbuf, "  100% ");
403        return pbuf;
404    }
405    /* else generate " [N/space]N.N% " string */
406    value = 1000 * value / total;
407    t = value / 100;
408    value = value % 100;
409    pbuf[0] = ' ';
410    pbuf[1] = t ? t + '0' : ' ';
411    pbuf[2] = '0' + (value / 10);
412    pbuf[3] = '.';
413    pbuf[4] = '0' + (value % 10);
414    pbuf[5] = '%';
415    pbuf[6] = ' ';
416    pbuf[7] = '\0';
417    return pbuf;
418}
419#endif
420
421#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
422static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
423{
424    /*
425     * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
426     */
427    unsigned total_diff;
428    jiffy_counts_t *p_jif, *p_prev_jif;
429    int i;
430# if ENABLE_FEATURE_TOP_SMP_CPU
431    int n_cpu_lines;
432# endif
433
434    /* using (unsigned) casts to make operations cheaper */
435# define  CALC_TOTAL_DIFF do { \
436    total_diff = (unsigned)(p_jif->total - p_prev_jif->total); \
437    if (total_diff == 0) total_diff = 1; \
438} while (0)
439
440# if ENABLE_FEATURE_TOP_DECIMALS
441#  define CALC_STAT(xxx) char xxx[8]
442#  define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff)
443#  define FMT "%s"
444# else
445#  define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff
446#  define SHOW_STAT(xxx) xxx
447#  define FMT "%4u%% "
448# endif
449
450# if !ENABLE_FEATURE_TOP_SMP_CPU
451    {
452        i = 1;
453        p_jif = &cur_jif;
454        p_prev_jif = &prev_jif;
455# else
456    /* Loop thru CPU(s) */
457    n_cpu_lines = smp_cpu_info ? num_cpus : 1;
458    if (n_cpu_lines > *lines_rem_p)
459        n_cpu_lines = *lines_rem_p;
460
461    for (i = 0; i < n_cpu_lines; i++) {
462        p_jif = &cpu_jif[i];
463        p_prev_jif = &cpu_prev_jif[i];
464# endif
465        CALC_TOTAL_DIFF;
466
467        { /* Need a block: CALC_STAT are declarations */
468            CALC_STAT(usr);
469            CALC_STAT(sys);
470            CALC_STAT(nic);
471            CALC_STAT(idle);
472            CALC_STAT(iowait);
473            CALC_STAT(irq);
474            CALC_STAT(softirq);
475            /*CALC_STAT(steal);*/
476
477            snprintf(scrbuf, scr_width,
478                /* Barely fits in 79 chars when in "decimals" mode. */
479# if ENABLE_FEATURE_TOP_SMP_CPU
480                "CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
481                (smp_cpu_info ? utoa(i) : ""),
482# else
483                "CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
484# endif
485                SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
486                SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
487                /*, SHOW_STAT(steal) - what is this 'steal' thing? */
488                /* I doubt anyone wants to know it */
489            );
490            puts(scrbuf);
491        }
492    }
493# undef SHOW_STAT
494# undef CALC_STAT
495# undef FMT
496    *lines_rem_p -= i;
497}
498#else  /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
499# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
500#endif
501
502static unsigned long display_header(int scr_width, int *lines_rem_p)
503{
504    FILE *fp;
505    char buf[80];
506    char scrbuf[80];
507    unsigned long total, used, mfree, shared, buffers, cached;
508
509    /* read memory info */
510    fp = xfopen_for_read("meminfo");
511
512    /*
513     * Old kernels (such as 2.4.x) had a nice summary of memory info that
514     * we could parse, however this is gone entirely in 2.6. Try parsing
515     * the old way first, and if that fails, parse each field manually.
516     *
517     * First, we read in the first line. Old kernels will have bogus
518     * strings we don't care about, whereas new kernels will start right
519     * out with MemTotal:
520     *                              -- PFM.
521     */
522    if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
523        fgets(buf, sizeof(buf), fp);    /* skip first line */
524
525        fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
526            &total, &used, &mfree, &shared, &buffers, &cached);
527        /* convert to kilobytes */
528        used /= 1024;
529        mfree /= 1024;
530        shared /= 1024;
531        buffers /= 1024;
532        cached /= 1024;
533        total /= 1024;
534    } else {
535        /*
536         * Revert to manual parsing, which incidentally already has the
537         * sizes in kilobytes. This should be safe for both 2.4 and
538         * 2.6.
539         */
540        fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
541
542        /*
543         * MemShared: is no longer present in 2.6. Report this as 0,
544         * to maintain consistent behavior with normal procps.
545         */
546        if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
547            shared = 0;
548
549        fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
550        fscanf(fp, "Cached: %lu %s\n", &cached, buf);
551
552        used = total - mfree;
553    }
554    fclose(fp);
555
556    /* output memory info */
557    if (scr_width > (int)sizeof(scrbuf))
558        scr_width = sizeof(scrbuf);
559    snprintf(scrbuf, scr_width,
560        "Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
561        used, mfree, shared, buffers, cached);
562    /* go to top & clear to the end of screen */
563    printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[J%s\n", scrbuf);
564    (*lines_rem_p)--;
565
566    /* Display CPU time split as percentage of total time
567     * This displays either a cumulative line or one line per CPU
568     */
569    display_cpus(scr_width, scrbuf, lines_rem_p);
570
571    /* read load average as a string */
572    buf[0] = '\0';
573    open_read_close("loadavg", buf, sizeof(buf) - 1);
574    buf[sizeof(buf) - 1] = '\n';
575    *strchr(buf, '\n') = '\0';
576    snprintf(scrbuf, scr_width, "Load average: %s", buf);
577    puts(scrbuf);
578    (*lines_rem_p)--;
579
580    return total;
581}
582
583static NOINLINE void display_process_list(int lines_rem, int scr_width)
584{
585    enum {
586        BITS_PER_INT = sizeof(int) * 8
587    };
588
589    top_status_t *s;
590    char vsz_str_buf[8];
591    unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */
592    /* xxx_shift and xxx_scale variables allow us to replace
593     * expensive divides with multiply and shift */
594    unsigned pmem_shift, pmem_scale, pmem_half;
595#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
596    unsigned tmp_unsigned;
597    unsigned pcpu_shift, pcpu_scale, pcpu_half;
598    unsigned busy_jifs;
599#endif
600
601    /* what info of the processes is shown */
602    printf(OPT_BATCH_MODE ? "%.*s" : "\033[7m%.*s\033[0m", scr_width,
603        "  PID  PPID USER     STAT   VSZ %VSZ"
604        IF_FEATURE_TOP_SMP_PROCESS(" CPU")
605        IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
606        " COMMAND");
607    lines_rem--;
608
609#if ENABLE_FEATURE_TOP_DECIMALS
610# define UPSCALE 1000
611# define CALC_STAT(name, val) div_t name = div((val), 10)
612# define SHOW_STAT(name) name.quot, '0'+name.rem
613# define FMT "%3u.%c"
614#else
615# define UPSCALE 100
616# define CALC_STAT(name, val) unsigned name = (val)
617# define SHOW_STAT(name) name
618# define FMT "%4u%%"
619#endif
620    /*
621     * %VSZ = s->vsz/MemTotal
622     */
623    pmem_shift = BITS_PER_INT-11;
624    pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
625    /* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
626    while (pmem_scale >= 512) {
627        pmem_scale /= 4;
628        pmem_shift -= 2;
629    }
630    pmem_half = (1U << pmem_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
631#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
632    busy_jifs = cur_jif.busy - prev_jif.busy;
633    /* This happens if there were lots of short-lived processes
634     * between two top updates (e.g. compilation) */
635    if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
636
637    /*
638     * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
639     * (pcpu is delta of sys+user time between samples)
640     */
641    /* (cur_jif.xxx - prev_jif.xxx) and s->pcpu are
642     * in 0..~64000 range (HZ*update_interval).
643     * we assume that unsigned is at least 32-bit.
644     */
645    pcpu_shift = 6;
646    pcpu_scale = UPSCALE*64 * (uint16_t)busy_jifs;
647    if (pcpu_scale == 0)
648        pcpu_scale = 1;
649    while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
650        pcpu_scale *= 4;
651        pcpu_shift += 2;
652    }
653    tmp_unsigned = (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu;
654    if (tmp_unsigned != 0)
655        pcpu_scale /= tmp_unsigned;
656    /* we want (s->pcpu * pcpu_scale) to never overflow */
657    while (pcpu_scale >= 1024) {
658        pcpu_scale /= 4;
659        pcpu_shift -= 2;
660    }
661    pcpu_half = (1U << pcpu_shift) / (ENABLE_FEATURE_TOP_DECIMALS ? 20 : 2);
662    /* printf(" pmem_scale=%u pcpu_scale=%u ", pmem_scale, pcpu_scale); */
663#endif
664
665    /* Ok, all preliminary data is ready, go through the list */
666    scr_width += 2; /* account for leading '\n' and trailing NUL */
667    if (lines_rem > ntop - G_scroll_ofs)
668        lines_rem = ntop - G_scroll_ofs;
669    s = top + G_scroll_ofs;
670    while (--lines_rem >= 0) {
671        unsigned col;
672        CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
673#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
674        CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
675#endif
676
677        if (s->vsz >= 100000)
678            sprintf(vsz_str_buf, "%6ldm", s->vsz/1024);
679        else
680            sprintf(vsz_str_buf, "%7ld", s->vsz);
681        /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
682        col = snprintf(line_buf, scr_width,
683                "\n" "%5u%6u %-8.8s %s%s" FMT
684                IF_FEATURE_TOP_SMP_PROCESS(" %3d")
685                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
686                " ",
687                s->pid, s->ppid, get_cached_username(s->uid),
688                s->state, vsz_str_buf,
689                SHOW_STAT(pmem)
690                IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
691                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
692        );
693        if ((int)(col + 1) < scr_width)
694            read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
695        fputs(line_buf, stdout);
696        /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
697            cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
698        s++;
699    }
700    /* printf(" %d", hist_iterations); */
701    bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
702    fflush_all();
703}
704#undef UPSCALE
705#undef SHOW_STAT
706#undef CALC_STAT
707#undef FMT
708
709static void clearmems(void)
710{
711    clear_username_cache();
712    free(top);
713    top = NULL;
714}
715
716#if ENABLE_FEATURE_USE_TERMIOS
717
718static void reset_term(void)
719{
720    if (!OPT_BATCH_MODE)
721        tcsetattr_stdin_TCSANOW(&initial_settings);
722    if (ENABLE_FEATURE_CLEAN_UP) {
723        clearmems();
724# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
725        free(prev_hist);
726# endif
727    }
728}
729
730static void sig_catcher(int sig)
731{
732    reset_term();
733    kill_myself_with_sig(sig);
734}
735
736#endif /* FEATURE_USE_TERMIOS */
737
738/*
739 * TOPMEM support
740 */
741
742typedef unsigned long mem_t;
743
744typedef struct topmem_status_t {
745    unsigned pid;
746    char comm[COMM_LEN];
747    /* vsz doesn't count /dev/xxx mappings except /dev/zero */
748    mem_t vsz     ;
749    mem_t vszrw   ;
750    mem_t rss     ;
751    mem_t rss_sh  ;
752    mem_t dirty   ;
753    mem_t dirty_sh;
754    mem_t stack   ;
755} topmem_status_t;
756
757enum { NUM_SORT_FIELD = 7 };
758
759#define topmem ((topmem_status_t*)top)
760
761#if ENABLE_FEATURE_TOPMEM
762
763static int topmem_sort(char *a, char *b)
764{
765    int n;
766    mem_t l, r;
767
768    n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
769    l = *(mem_t*)(a + n);
770    r = *(mem_t*)(b + n);
771    if (l == r) {
772        l = ((topmem_status_t*)a)->dirty;
773        r = ((topmem_status_t*)b)->dirty;
774    }
775    /* We want to avoid unsigned->signed and truncation errors */
776    /* l>r: -1, l=r: 0, l<r: 1 */
777    n = (l > r) ? -1 : (l != r);
778    return inverted ? -n : n;
779}
780
781/* display header info (meminfo / loadavg) */
782static void display_topmem_header(int scr_width, int *lines_rem_p)
783{
784    enum {
785        TOTAL = 0, MFREE, BUF, CACHE,
786        SWAPTOTAL, SWAPFREE, DIRTY,
787        MWRITE, ANON, MAP, SLAB,
788        NUM_FIELDS
789    };
790    static const char match[NUM_FIELDS][12] = {
791        "\x09" "MemTotal:",  // TOTAL
792        "\x08" "MemFree:",   // MFREE
793        "\x08" "Buffers:",   // BUF
794        "\x07" "Cached:",    // CACHE
795        "\x0a" "SwapTotal:", // SWAPTOTAL
796        "\x09" "SwapFree:",  // SWAPFREE
797        "\x06" "Dirty:",     // DIRTY
798        "\x0a" "Writeback:", // MWRITE
799        "\x0a" "AnonPages:", // ANON
800        "\x07" "Mapped:",    // MAP
801        "\x05" "Slab:",      // SLAB
802    };
803    char meminfo_buf[4 * 1024];
804    const char *Z[NUM_FIELDS];
805    unsigned i;
806    int sz;
807
808    for (i = 0; i < NUM_FIELDS; i++)
809        Z[i] = "?";
810
811    /* read memory info */
812    sz = open_read_close("meminfo", meminfo_buf, sizeof(meminfo_buf) - 1);
813    if (sz >= 0) {
814        char *p = meminfo_buf;
815        meminfo_buf[sz] = '\0';
816        /* Note that fields always appear in the match[] order */
817        for (i = 0; i < NUM_FIELDS; i++) {
818            char *found = strstr(p, match[i] + 1);
819            if (found) {
820                /* Cut "NNNN" out of "    NNNN kb" */
821                char *s = skip_whitespace(found + match[i][0]);
822                p = skip_non_whitespace(s);
823                *p++ = '\0';
824                Z[i] = s;
825            }
826        }
827    }
828
829    snprintf(line_buf, LINE_BUF_SIZE,
830        "Mem total:%s anon:%s map:%s free:%s",
831        Z[TOTAL], Z[ANON], Z[MAP], Z[MFREE]);
832    printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf);
833
834    snprintf(line_buf, LINE_BUF_SIZE,
835        " slab:%s buf:%s cache:%s dirty:%s write:%s",
836        Z[SLAB], Z[BUF], Z[CACHE], Z[DIRTY], Z[MWRITE]);
837    printf("%.*s\n", scr_width, line_buf);
838
839    snprintf(line_buf, LINE_BUF_SIZE,
840        "Swap total:%s free:%s", // TODO: % used?
841        Z[SWAPTOTAL], Z[SWAPFREE]);
842    printf("%.*s\n", scr_width, line_buf);
843
844    (*lines_rem_p) -= 3;
845}
846
847static void ulltoa6_and_space(unsigned long long ul, char buf[6])
848{
849    /* see http://en.wikipedia.org/wiki/Tera */
850    smart_ulltoa5(ul, buf, " mgtpezy");
851    buf[5] = ' ';
852}
853
854static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
855{
856#define HDR_STR "  PID   VSZ VSZRW   RSS (SHR) DIRTY (SHR) STACK"
857#define MIN_WIDTH sizeof(HDR_STR)
858    const topmem_status_t *s = topmem + G_scroll_ofs;
859
860    display_topmem_header(scr_width, &lines_rem);
861    strcpy(line_buf, HDR_STR " COMMAND");
862    line_buf[11 + sort_field * 6] = "^_"[inverted];
863    printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
864    lines_rem--;
865
866    if (lines_rem > ntop - G_scroll_ofs)
867        lines_rem = ntop - G_scroll_ofs;
868    while (--lines_rem >= 0) {
869        /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
870        ulltoa6_and_space(s->pid     , &line_buf[0*6]);
871        ulltoa6_and_space(s->vsz     , &line_buf[1*6]);
872        ulltoa6_and_space(s->vszrw   , &line_buf[2*6]);
873        ulltoa6_and_space(s->rss     , &line_buf[3*6]);
874        ulltoa6_and_space(s->rss_sh  , &line_buf[4*6]);
875        ulltoa6_and_space(s->dirty   , &line_buf[5*6]);
876        ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]);
877        ulltoa6_and_space(s->stack   , &line_buf[7*6]);
878        line_buf[8*6] = '\0';
879        if (scr_width > (int)MIN_WIDTH) {
880            read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
881        }
882        printf("\n""%.*s", scr_width, line_buf);
883        s++;
884    }
885    bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
886    fflush_all();
887#undef HDR_STR
888#undef MIN_WIDTH
889}
890
891#else
892void display_topmem_process_list(int lines_rem, int scr_width);
893int topmem_sort(char *a, char *b);
894#endif /* TOPMEM */
895
896/*
897 * end TOPMEM support
898 */
899
900enum {
901    TOP_MASK = 0
902        | PSSCAN_PID
903        | PSSCAN_PPID
904        | PSSCAN_VSZ
905        | PSSCAN_STIME
906        | PSSCAN_UTIME
907        | PSSCAN_STATE
908        | PSSCAN_COMM
909        | PSSCAN_CPU
910        | PSSCAN_UIDGID,
911    TOPMEM_MASK = 0
912        | PSSCAN_PID
913        | PSSCAN_SMAPS
914        | PSSCAN_COMM,
915    EXIT_MASK = (unsigned)-1,
916};
917
918#if ENABLE_FEATURE_USE_TERMIOS
919static unsigned handle_input(unsigned scan_mask, unsigned interval)
920{
921    struct pollfd pfd[1];
922
923    if (option_mask32 & OPT_EOF) {
924        /* EOF on stdin ("top </dev/null") */
925        sleep(interval);
926        return scan_mask;
927    }
928
929    pfd[0].fd = 0;
930    pfd[0].events = POLLIN;
931
932    while (1) {
933        int32_t c;
934
935        c = read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
936        if (c == -1 && errno != EAGAIN) {
937            /* error/EOF */
938            option_mask32 |= OPT_EOF;
939            break;
940        }
941        interval = 0;
942
943        if (c == initial_settings.c_cc[VINTR])
944            return EXIT_MASK;
945        if (c == initial_settings.c_cc[VEOF])
946            return EXIT_MASK;
947
948        if (c == KEYCODE_UP) {
949            G_scroll_ofs--;
950            goto normalize_ofs;
951        }
952        if (c == KEYCODE_DOWN) {
953            G_scroll_ofs++;
954            goto normalize_ofs;
955        }
956        if (c == KEYCODE_HOME) {
957            G_scroll_ofs = 0;
958            break;
959        }
960        if (c == KEYCODE_END) {
961            G_scroll_ofs = ntop - G.lines / 2;
962            goto normalize_ofs;
963        }
964        if (c == KEYCODE_PAGEUP) {
965            G_scroll_ofs -= G.lines / 2;
966            goto normalize_ofs;
967        }
968        if (c == KEYCODE_PAGEDOWN) {
969            G_scroll_ofs += G.lines / 2;
970 normalize_ofs:
971            if (G_scroll_ofs >= ntop)
972                G_scroll_ofs = ntop - 1;
973            if (G_scroll_ofs < 0)
974                G_scroll_ofs = 0;
975            break;
976        }
977
978        c |= 0x20; /* lowercase */
979        if (c == 'q')
980            return EXIT_MASK;
981
982        if (c == 'n') {
983            IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
984            sort_function[0] = pid_sort;
985            continue;
986        }
987        if (c == 'm') {
988            IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
989            sort_function[0] = mem_sort;
990# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
991            sort_function[1] = pcpu_sort;
992            sort_function[2] = time_sort;
993# endif
994            continue;
995        }
996# if ENABLE_FEATURE_SHOW_THREADS
997        if (c == 'h'
998        IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
999        ) {
1000            scan_mask ^= PSSCAN_TASKS;
1001            continue;
1002        }
1003# endif
1004# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1005        if (c == 'p') {
1006            IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1007            sort_function[0] = pcpu_sort;
1008            sort_function[1] = mem_sort;
1009            sort_function[2] = time_sort;
1010            continue;
1011        }
1012        if (c == 't') {
1013            IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1014            sort_function[0] = time_sort;
1015            sort_function[1] = mem_sort;
1016            sort_function[2] = pcpu_sort;
1017            continue;
1018        }
1019#  if ENABLE_FEATURE_TOPMEM
1020        if (c == 's') {
1021            scan_mask = TOPMEM_MASK;
1022            free(prev_hist);
1023            prev_hist = NULL;
1024            prev_hist_count = 0;
1025            sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1026            continue;
1027        }
1028#  endif
1029        if (c == 'r') {
1030            inverted ^= 1;
1031            continue;
1032        }
1033#  if ENABLE_FEATURE_TOP_SMP_CPU
1034        /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
1035        if (c == 'c' || c == '1') {
1036            /* User wants to toggle per cpu <> aggregate */
1037            if (smp_cpu_info) {
1038                free(cpu_prev_jif);
1039                free(cpu_jif);
1040                cpu_jif = &cur_jif;
1041                cpu_prev_jif = &prev_jif;
1042            } else {
1043                /* Prepare for xrealloc() */
1044                cpu_jif = cpu_prev_jif = NULL;
1045            }
1046            num_cpus = 0;
1047            smp_cpu_info = !smp_cpu_info;
1048            get_jiffy_counts();
1049            continue;
1050        }
1051#  endif
1052# endif
1053        break; /* unknown key -> force refresh */
1054    }
1055
1056    return scan_mask;
1057}
1058#endif
1059
1060//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU
1061//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__
1062//usage:#else
1063//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...)
1064//usage:#endif
1065//usage:#define top_trivial_usage
1066//usage:       "[-b] [-nCOUNT] [-dSECONDS]" IF_FEATURE_TOPMEM(" [-m]")
1067//usage:#define top_full_usage "\n\n"
1068//usage:       "Provide a view of process activity in real time."
1069//usage:   "\n""Read the status of all processes from /proc each SECONDS"
1070//usage:   "\n""and display a screenful of them."
1071//usage:   "\n""Keys:"
1072//usage:   "\n""    N/M"
1073//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/P")
1074//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/T")
1075//usage:           ": " IF_FEATURE_TOPMEM("show CPU usage, ") "sort by pid/mem"
1076//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu")
1077//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time")
1078//usage:    IF_FEATURE_TOPMEM(
1079//usage:   "\n""    S: show memory"
1080//usage:    )
1081//usage:   "\n""    R: reverse sort"
1082//usage:    IF_SHOW_THREADS_OR_TOP_SMP(
1083//usage:   "\n""    "
1084//usage:                IF_FEATURE_SHOW_THREADS("H: toggle threads")
1085//usage:                IF_FEATURE_SHOW_THREADS(IF_FEATURE_TOP_SMP_CPU(", "))
1086//usage:                IF_FEATURE_TOP_SMP_CPU("1: toggle SMP")
1087//usage:    )
1088//usage:   "\n""    Q,^C: exit"
1089//usage:   "\n"
1090//usage:   "\n""Options:"
1091//usage:   "\n""    -b  Batch mode"
1092//usage:   "\n""    -n N    Exit after N iterations"
1093//usage:   "\n""    -d N    Delay between updates"
1094//usage:    IF_FEATURE_TOPMEM(
1095//usage:   "\n""    -m  Same as 's' key"
1096//usage:    )
1097
1098/* Interactive testing:
1099 * echo sss | ./busybox top
1100 * - shows memory screen
1101 * echo sss | ./busybox top -bn1 >mem
1102 * - saves memory screen - the *whole* list, not first NROWS processes!
1103 * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z
1104 * - saves several different screens, and exits
1105 *
1106 * TODO: -i STRING param as a better alternative?
1107 */
1108
1109int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1110int top_main(int argc UNUSED_PARAM, char **argv)
1111{
1112    int iterations;
1113    unsigned col;
1114    unsigned interval;
1115    char *str_interval, *str_iterations;
1116    unsigned scan_mask = TOP_MASK;
1117#if ENABLE_FEATURE_USE_TERMIOS
1118    struct termios new_settings;
1119#endif
1120
1121    INIT_G();
1122
1123    interval = 5; /* default update interval is 5 seconds */
1124    iterations = 0; /* infinite */
1125#if ENABLE_FEATURE_TOP_SMP_CPU
1126    /*num_cpus = 0;*/
1127    /*smp_cpu_info = 0;*/  /* to start with show aggregate */
1128    cpu_jif = &cur_jif;
1129    cpu_prev_jif = &prev_jif;
1130#endif
1131
1132    /* all args are options; -n NUM */
1133    opt_complementary = "-"; /* options can be specified w/o dash */
1134    col = getopt32(argv, "d:n:b"IF_FEATURE_TOPMEM("m"), &str_interval, &str_iterations);
1135#if ENABLE_FEATURE_TOPMEM
1136    if (col & OPT_m) /* -m (busybox specific) */
1137        scan_mask = TOPMEM_MASK;
1138#endif
1139    if (col & OPT_d) {
1140        /* work around for "-d 1" -> "-d -1" done by getopt32
1141         * (opt_complementary == "-" does this) */
1142        if (str_interval[0] == '-')
1143            str_interval++;
1144        /* Need to limit it to not overflow poll timeout */
1145        interval = xatou16(str_interval);
1146    }
1147    if (col & OPT_n) {
1148        if (str_iterations[0] == '-')
1149            str_iterations++;
1150        iterations = xatou(str_iterations);
1151    }
1152
1153    /* change to /proc */
1154    xchdir("/proc");
1155
1156#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1157    sort_function[0] = pcpu_sort;
1158    sort_function[1] = mem_sort;
1159    sort_function[2] = time_sort;
1160#else
1161    sort_function[0] = mem_sort;
1162#endif
1163
1164    if (OPT_BATCH_MODE) {
1165        option_mask32 |= OPT_EOF;
1166    }
1167#if ENABLE_FEATURE_USE_TERMIOS
1168    else {
1169        tcgetattr(0, (void *) &initial_settings);
1170        memcpy(&new_settings, &initial_settings, sizeof(new_settings));
1171        /* unbuffered input, turn off echo */
1172        new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL);
1173        tcsetattr_stdin_TCSANOW(&new_settings);
1174    }
1175
1176    bb_signals(BB_FATAL_SIGS, sig_catcher);
1177
1178    /* Eat initial input, if any */
1179    scan_mask = handle_input(scan_mask, 0);
1180#endif
1181
1182    while (scan_mask != EXIT_MASK) {
1183        procps_status_t *p = NULL;
1184
1185        if (OPT_BATCH_MODE) {
1186            G.lines = INT_MAX;
1187            col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1188        } else {
1189            G.lines = 24; /* default */
1190            col = 79;
1191#if ENABLE_FEATURE_USE_TERMIOS
1192            /* We output to stdout, we need size of stdout (not stdin)! */
1193            get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
1194            if (G.lines < 5 || col < 10) {
1195                sleep(interval);
1196                continue;
1197            }
1198#endif
1199            if (col > LINE_BUF_SIZE - 2)
1200                col = LINE_BUF_SIZE - 2;
1201        }
1202
1203        /* read process IDs & status for all the processes */
1204        ntop = 0;
1205        while ((p = procps_scan(p, scan_mask)) != NULL) {
1206            int n;
1207#if ENABLE_FEATURE_TOPMEM
1208            if (scan_mask != TOPMEM_MASK)
1209#endif
1210            {
1211                n = ntop;
1212                top = xrealloc_vector(top, 6, ntop++);
1213                top[n].pid = p->pid;
1214                top[n].ppid = p->ppid;
1215                top[n].vsz = p->vsz;
1216#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1217                top[n].ticks = p->stime + p->utime;
1218#endif
1219                top[n].uid = p->uid;
1220                strcpy(top[n].state, p->state);
1221                strcpy(top[n].comm, p->comm);
1222#if ENABLE_FEATURE_TOP_SMP_PROCESS
1223                top[n].last_seen_on_cpu = p->last_seen_on_cpu;
1224#endif
1225            }
1226#if ENABLE_FEATURE_TOPMEM
1227            else { /* TOPMEM */
1228                if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
1229                    continue; /* kernel threads are ignored */
1230                n = ntop;
1231                /* No bug here - top and topmem are the same */
1232                top = xrealloc_vector(topmem, 6, ntop++);
1233                strcpy(topmem[n].comm, p->comm);
1234                topmem[n].pid      = p->pid;
1235                topmem[n].vsz      = p->smaps.mapped_rw + p->smaps.mapped_ro;
1236                topmem[n].vszrw    = p->smaps.mapped_rw;
1237                topmem[n].rss_sh   = p->smaps.shared_clean + p->smaps.shared_dirty;
1238                topmem[n].rss      = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
1239                topmem[n].dirty    = p->smaps.private_dirty + p->smaps.shared_dirty;
1240                topmem[n].dirty_sh = p->smaps.shared_dirty;
1241                topmem[n].stack    = p->smaps.stack;
1242            }
1243#endif
1244        } /* end of "while we read /proc" */
1245        if (ntop == 0) {
1246            bb_error_msg("no process info in /proc");
1247            break;
1248        }
1249
1250        if (scan_mask != TOPMEM_MASK) {
1251#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1252            if (!prev_hist_count) {
1253                do_stats();
1254                usleep(100000);
1255                clearmems();
1256                continue;
1257            }
1258            do_stats();
1259            /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
1260            qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
1261#else
1262            qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
1263#endif
1264        }
1265#if ENABLE_FEATURE_TOPMEM
1266        else { /* TOPMEM */
1267            qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1268        }
1269#endif
1270        if (scan_mask != TOPMEM_MASK)
1271            display_process_list(G.lines, col);
1272#if ENABLE_FEATURE_TOPMEM
1273        else
1274            display_topmem_process_list(G.lines, col);
1275#endif
1276        clearmems();
1277        if (iterations >= 0 && !--iterations)
1278            break;
1279#if !ENABLE_FEATURE_USE_TERMIOS
1280        sleep(interval);
1281#else
1282        scan_mask = handle_input(scan_mask, interval);
1283#endif /* FEATURE_USE_TERMIOS */
1284    } /* end of "while (not Q)" */
1285
1286    bb_putchar('\n');
1287#if ENABLE_FEATURE_USE_TERMIOS
1288    reset_term();
1289#endif
1290    return EXIT_SUCCESS;
1291}
Note: See TracBrowser for help on using the repository browser.