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