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

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