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                int n;
 693                char *ppu;
 694                char ppubuf[sizeof(int)*3 * 2 + 12];
 695                char vsz_str_buf[8];
 696                unsigned col;
 697
 698                CALC_STAT(pmem, (s->vsz*pmem_scale + pmem_half) >> pmem_shift);
 699#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
 700                CALC_STAT(pcpu, (s->pcpu*pcpu_scale + pcpu_half) >> pcpu_shift);
 701#endif
 702
 703                smart_ulltoa5(s->vsz, vsz_str_buf, " mgtpezy");
 704                /* PID PPID USER STAT VSZ %VSZ [%CPU] COMMAND */
 705                n = sprintf(ppubuf, "%5u %5u %-8.8s", s->pid, s->ppid, get_cached_username(s->uid));
 706                ppu = ppubuf;
 707                if (n != 6+6+8) {
 708                        /* Format PID PPID USER part into 6+6+8 chars:
 709                         * shrink PID/PPID if possible, then truncate USER
 710                         */
 711                        char *p, *pp;
 712                        if (*ppu == ' ') {
 713                                do {
 714                                        ppu++, n--;
 715                                        if (n == 6+6+8)
 716                                                goto shortened;
 717                                } while (*ppu == ' ');
 718                        }
 719                        pp = p = skip_non_whitespace(ppu) + 1;
 720                        if (*p == ' ') {
 721                                do
 722                                        p++, n--;
 723                                while (n != 6+6+8 && *p == ' ');
 724                                overlapping_strcpy(pp, p); /* shrink PPID */
 725                        }
 726                        ppu[6+6+8] = '\0'; /* truncate USER */
 727                }
 728 shortened:
 729                col = snprintf(line_buf, scr_width,
 730                                "\n" "%s %s  %.5s" FMT
 731                                IF_FEATURE_TOP_SMP_PROCESS(" %3d")
 732                                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
 733                                " ",
 734                                ppu,
 735                                s->state, vsz_str_buf,
 736                                SHOW_STAT(pmem)
 737                                IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
 738                                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
 739                );
 740                if ((int)(scr_width - col) > 1)
 741                        read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
 742                fputs_stdout(line_buf);
 743                /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
 744                        cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
 745                s++;
 746        }
 747        /* printf(" %d", hist_iterations); */
 748        bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
 749        fflush_all();
 750}
 751#undef UPSCALE
 752#undef SHOW_STAT
 753#undef CALC_STAT
 754#undef FMT
 755
 756static void clearmems(void)
 757{
 758        clear_username_cache();
 759        free(top);
 760        top = NULL;
 761}
 762
 763#if ENABLE_FEATURE_TOP_INTERACTIVE
 764static void reset_term(void)
 765{
 766        if (!OPT_BATCH_MODE)
 767                tcsetattr_stdin_TCSANOW(&initial_settings);
 768}
 769
 770static void sig_catcher(int sig)
 771{
 772        reset_term();
 773        kill_myself_with_sig(sig);
 774}
 775#endif /* FEATURE_TOP_INTERACTIVE */
 776
 777/*
 778 * TOPMEM support
 779 */
 780
 781typedef unsigned long mem_t;
 782
 783typedef struct topmem_status_t {
 784        unsigned pid;
 785        char comm[COMM_LEN];
 786        /* vsz doesn't count /dev/xxx mappings except /dev/zero */
 787        mem_t vsz     ;
 788        mem_t vszrw   ;
 789        mem_t rss     ;
 790        mem_t rss_sh  ;
 791        mem_t dirty   ;
 792        mem_t dirty_sh;
 793        mem_t stack   ;
 794} topmem_status_t;
 795
 796enum { NUM_SORT_FIELD = 7 };
 797
 798#define topmem ((topmem_status_t*)top)
 799
 800#if ENABLE_FEATURE_TOPMEM
 801
 802static int topmem_sort(char *a, char *b)
 803{
 804        int n;
 805        mem_t l, r;
 806
 807        n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
 808        l = *(mem_t*)(a + n);
 809        r = *(mem_t*)(b + n);
 810        if (l == r) {
 811                l = ((topmem_status_t*)a)->dirty;
 812                r = ((topmem_status_t*)b)->dirty;
 813        }
 814        /* We want to avoid unsigned->signed and truncation errors */
 815        /* l>r: -1, l=r: 0, l<r: 1 */
 816        n = (l > r) ? -1 : (l != r);
 817        return inverted ? -n : n;
 818}
 819
 820/* display header info (meminfo / loadavg) */
 821static void display_topmem_header(int scr_width, int *lines_rem_p)
 822{
 823        unsigned long meminfo[MI_MAX];
 824
 825        parse_meminfo(meminfo);
 826
 827        snprintf(line_buf, LINE_BUF_SIZE,
 828                "Mem total:%lu anon:%lu map:%lu free:%lu",
 829                meminfo[MI_MEMTOTAL],
 830                meminfo[MI_ANONPAGES],
 831                meminfo[MI_MAPPED],
 832                meminfo[MI_MEMFREE]);
 833        printf(OPT_BATCH_MODE ? "%.*s\n" : ESC"[H" ESC"[J" "%.*s\n", scr_width, line_buf);
 834
 835        snprintf(line_buf, LINE_BUF_SIZE,
 836                " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
 837                meminfo[MI_SLAB],
 838                meminfo[MI_BUFFERS],
 839                meminfo[MI_CACHED],
 840                meminfo[MI_DIRTY],
 841                meminfo[MI_WRITEBACK]);
 842        printf("%.*s\n", scr_width, line_buf);
 843
 844        snprintf(line_buf, LINE_BUF_SIZE,
 845                "Swap total:%lu free:%lu", // TODO: % used?
 846                meminfo[MI_SWAPTOTAL],
 847                meminfo[MI_SWAPFREE]);
 848        printf("%.*s\n", scr_width, line_buf);
 849
 850        (*lines_rem_p) -= 3;
 851}
 852
 853static void ulltoa6_and_space(unsigned long long ul, char buf[6])
 854{
 855        /* see http://en.wikipedia.org/wiki/Tera */
 856        smart_ulltoa5(ul, buf, " mgtpezy")[0] = ' ';
 857}
 858
 859static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
 860{
 861#define HDR_STR "  PID   VSZ VSZRW   RSS (SHR) DIRTY (SHR) STACK"
 862#define MIN_WIDTH sizeof(HDR_STR)
 863        const topmem_status_t *s = topmem + G_scroll_ofs;
 864        char *cp, ch;
 865
 866        display_topmem_header(scr_width, &lines_rem);
 867
 868        strcpy(line_buf, HDR_STR " COMMAND");
 869        /* Mark the ^FIELD^ we sort by */
 870        cp = &line_buf[5 + sort_field * 6];
 871        ch = "^_"[inverted];
 872        cp[6] = ch;
 873        do *cp++ = ch; while (*cp == ' ');
 874
 875        printf(OPT_BATCH_MODE ? "%.*s" : ESC"[7m" "%.*s" ESC"[m", scr_width, line_buf);
 876        lines_rem--;
 877
 878        if (lines_rem > ntop - G_scroll_ofs)
 879                lines_rem = ntop - G_scroll_ofs;
 880        while (--lines_rem >= 0) {
 881                /* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
 882                int n = sprintf(line_buf, "%5u ", s->pid);
 883                ulltoa6_and_space(s->vsz     , &line_buf[1*6]);
 884                if (n > 7 || (n == 7 && line_buf[6] != ' '))
 885                        /* PID and VSZ are clumped together, truncate PID */
 886                        line_buf[5] = '.';
 887                ulltoa6_and_space(s->vszrw   , &line_buf[2*6]);
 888                ulltoa6_and_space(s->rss     , &line_buf[3*6]);
 889                ulltoa6_and_space(s->rss_sh  , &line_buf[4*6]);
 890                ulltoa6_and_space(s->dirty   , &line_buf[5*6]);
 891                ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]);
 892                ulltoa6_and_space(s->stack   , &line_buf[7*6]);
 893                line_buf[8*6] = '\0';
 894                if (scr_width > (int)MIN_WIDTH) {
 895                        read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
 896                }
 897                printf("\n""%.*s", scr_width, line_buf);
 898                s++;
 899        }
 900        bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
 901        fflush_all();
 902#undef HDR_STR
 903#undef MIN_WIDTH
 904}
 905
 906#else
 907void display_topmem_process_list(int lines_rem, int scr_width);
 908int topmem_sort(char *a, char *b);
 909#endif /* TOPMEM */
 910
 911/*
 912 * end TOPMEM support
 913 */
 914
 915enum {
 916        TOP_MASK = 0
 917                | PSSCAN_PID
 918                | PSSCAN_PPID
 919                | PSSCAN_VSZ
 920                | PSSCAN_STIME
 921                | PSSCAN_UTIME
 922                | PSSCAN_STATE
 923                | PSSCAN_COMM
 924                | PSSCAN_CPU
 925                | PSSCAN_UIDGID,
 926        TOPMEM_MASK = 0
 927                | PSSCAN_PID
 928                | PSSCAN_SMAPS
 929                | PSSCAN_COMM,
 930        EXIT_MASK = 0,
 931        NO_RESCAN_MASK = (unsigned)-1,
 932};
 933
 934#if ENABLE_FEATURE_TOP_INTERACTIVE
 935static unsigned handle_input(unsigned scan_mask, duration_t interval)
 936{
 937        if (option_mask32 & OPT_EOF) {
 938                /* EOF on stdin ("top </dev/null") */
 939                sleep_for_duration(interval);
 940                return scan_mask;
 941        }
 942
 943        while (1) {
 944                int32_t c;
 945
 946                c = safe_read_key(STDIN_FILENO, G.kbd_input, interval * 1000);
 947                if (c == -1 && errno != EAGAIN) {
 948                        /* error/EOF */
 949                        option_mask32 |= OPT_EOF;
 950                        break;
 951                }
 952                interval = 0;
 953
 954                if (c == initial_settings.c_cc[VINTR])
 955                        return EXIT_MASK;
 956                if (c == initial_settings.c_cc[VEOF])
 957                        return EXIT_MASK;
 958
 959                if (c == KEYCODE_UP) {
 960                        G_scroll_ofs--;
 961                        goto normalize_ofs;
 962                }
 963                if (c == KEYCODE_DOWN) {
 964                        G_scroll_ofs++;
 965                        goto normalize_ofs;
 966                }
 967                if (c == KEYCODE_HOME) {
 968                        G_scroll_ofs = 0;
 969                        goto normalize_ofs;
 970                }
 971                if (c == KEYCODE_END) {
 972                        G_scroll_ofs = ntop - G.lines / 2;
 973                        goto normalize_ofs;
 974                }
 975                if (c == KEYCODE_PAGEUP) {
 976                        G_scroll_ofs -= G.lines / 2;
 977                        goto normalize_ofs;
 978                }
 979                if (c == KEYCODE_PAGEDOWN) {
 980                        G_scroll_ofs += G.lines / 2;
 981 normalize_ofs:
 982                        if (G_scroll_ofs >= ntop)
 983                                G_scroll_ofs = ntop - 1;
 984                        if (G_scroll_ofs < 0)
 985                                G_scroll_ofs = 0;
 986                        return NO_RESCAN_MASK;
 987                }
 988
 989                c |= 0x20; /* lowercase */
 990                if (c == 'q')
 991                        return EXIT_MASK;
 992
 993                if (c == 'n') {
 994                        IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
 995                        sort_function[0] = pid_sort;
 996                        continue;
 997                }
 998                if (c == 'm') {
 999                        IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1000                        sort_function[0] = mem_sort;
1001# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1002                        sort_function[1] = pcpu_sort;
1003                        sort_function[2] = time_sort;
1004# endif
1005                        continue;
1006                }
1007# if ENABLE_FEATURE_SHOW_THREADS
1008                if (c == 'h'
1009                IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK)
1010                ) {
1011                        scan_mask ^= PSSCAN_TASKS;
1012#  if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1013                        free(prev_hist);
1014                        prev_hist = NULL;
1015                        prev_hist_count = 0;
1016#   endif
1017                        continue;
1018                }
1019# endif
1020# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1021                if (c == 'p') {
1022                        IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1023                        sort_function[0] = pcpu_sort;
1024                        sort_function[1] = mem_sort;
1025                        sort_function[2] = time_sort;
1026                        continue;
1027                }
1028                if (c == 't') {
1029                        IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;)
1030                        sort_function[0] = time_sort;
1031                        sort_function[1] = mem_sort;
1032                        sort_function[2] = pcpu_sort;
1033                        continue;
1034                }
1035#  if ENABLE_FEATURE_TOPMEM
1036                if (c == 's') {
1037                        scan_mask = TOPMEM_MASK;
1038                        sort_field = (sort_field + 1) % NUM_SORT_FIELD;
1039                        free(prev_hist);
1040                        prev_hist = NULL;
1041                        prev_hist_count = 0;
1042                        continue;
1043                }
1044#  endif
1045                if (c == 'r') {
1046                        inverted ^= 1;
1047                        continue;
1048                }
1049#  if ENABLE_FEATURE_TOP_SMP_CPU
1050                /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */
1051                if (c == 'c' || c == '1') {
1052                        /* User wants to toggle per cpu <> aggregate */
1053                        if (smp_cpu_info) {
1054                                free(cpu_prev_jif);
1055                                free(cpu_jif);
1056                                cpu_jif = &cur_jif;
1057                                cpu_prev_jif = &prev_jif;
1058                        } else {
1059                                /* Prepare for xrealloc() */
1060                                cpu_jif = cpu_prev_jif = NULL;
1061                        }
1062                        num_cpus = 0;
1063                        smp_cpu_info = !smp_cpu_info;
1064                        get_jiffy_counts();
1065                        continue;
1066                }
1067#  endif
1068# endif
1069                break; /* unknown key -> force refresh */
1070        }
1071
1072        return scan_mask;
1073}
1074#endif
1075
1076//usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU
1077//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__
1078//usage:#else
1079//usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...)
1080//usage:#endif
1081//usage:#define top_trivial_usage
1082//usage:       "[-b"IF_FEATURE_TOPMEM("m")IF_FEATURE_SHOW_THREADS("H")"]"
1083//usage:       " [-n COUNT] [-d SECONDS]"
1084//usage:#define top_full_usage "\n\n"
1085//usage:       "Show a view of process activity in real time."
1086//usage:   "\n""Read the status of all processes from /proc each SECONDS"
1087//usage:   "\n""and show a screenful of them."
1088//usage:   "\n"
1089//usage:        IF_FEATURE_TOP_INTERACTIVE(
1090//usage:       "Keys:"
1091//usage:   "\n""        N/M"
1092//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/P")
1093//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/T")
1094//usage:           ": " IF_FEATURE_TOPMEM("show CPU usage, ") "sort by pid/mem"
1095//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu")
1096//usage:                IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time")
1097//usage:        IF_FEATURE_TOPMEM(
1098//usage:   "\n""        S: show memory"
1099//usage:        )
1100//usage:   "\n""        R: reverse sort"
1101//usage:        IF_SHOW_THREADS_OR_TOP_SMP(
1102//usage:   "\n""        "
1103//usage:                IF_FEATURE_SHOW_THREADS("H: toggle threads")
1104//usage:                IF_FEATURE_SHOW_THREADS(IF_FEATURE_TOP_SMP_CPU(", "))
1105//usage:                IF_FEATURE_TOP_SMP_CPU("1: toggle SMP")
1106//usage:        )
1107//usage:   "\n""        Q,^C: exit"
1108//usage:   "\n""Options:"
1109//usage:        )
1110//usage:   "\n""        -b      Batch mode"
1111//usage:   "\n""        -n N    Exit after N iterations"
1112//usage:   "\n""        -d SEC  Delay between updates"
1113//usage:        IF_FEATURE_TOPMEM(
1114//usage:   "\n""        -m      Same as 's' key"
1115//usage:        )
1116//usage:        IF_FEATURE_SHOW_THREADS(
1117//usage:   "\n""        -H      Show threads"
1118//usage:        )
1119
1120/* Interactive testing:
1121 * echo sss | ./busybox top
1122 * - shows memory screen
1123 * echo sss | ./busybox top -bn1 >mem
1124 * - saves memory screen - the *whole* list, not first NROWS processes!
1125 * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z
1126 * - saves several different screens, and exits
1127 *
1128 * TODO: -i STRING param as a better alternative?
1129 */
1130
1131int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1132int top_main(int argc UNUSED_PARAM, char **argv)
1133{
1134        duration_t interval;
1135        int iterations;
1136        unsigned col;
1137        char *str_interval, *str_iterations;
1138        unsigned scan_mask = TOP_MASK;
1139
1140        INIT_G();
1141
1142        interval = 5; /* default update interval is 5 seconds */
1143        iterations = 0; /* infinite */
1144#if ENABLE_FEATURE_TOP_SMP_CPU
1145        /*num_cpus = 0;*/
1146        /*smp_cpu_info = 0;*/  /* to start with show aggregate */
1147        cpu_jif = &cur_jif;
1148        cpu_prev_jif = &prev_jif;
1149#endif
1150
1151        /* all args are options; -n NUM */
1152        make_all_argv_opts(argv); /* options can be specified w/o dash */
1153        col = getopt32(argv, "d:n:bHm", &str_interval, &str_iterations);
1154        /* NB: -m and -H are accepted even if not configured */
1155#if ENABLE_FEATURE_TOPMEM
1156        if (col & OPT_m) /* -m (busybox specific) */
1157                scan_mask = TOPMEM_MASK;
1158#endif
1159        if (col & OPT_d) {
1160                /* work around for "-d 1" -> "-d -1" done by make_all_argv_opts() */
1161                if (str_interval[0] == '-')
1162                        str_interval++;
1163                interval = parse_duration_str(str_interval);
1164                /* Need to limit it to not overflow poll timeout */
1165                if (interval > INT_MAX / 1000)
1166                        interval = INT_MAX / 1000;
1167        }
1168        if (col & OPT_n) {
1169                if (str_iterations[0] == '-')
1170                        str_iterations++;
1171                iterations = xatou(str_iterations);
1172        }
1173#if ENABLE_FEATURE_SHOW_THREADS
1174        if (col & OPT_H) {
1175                scan_mask |= PSSCAN_TASKS;
1176        }
1177#endif
1178
1179        /* change to /proc */
1180        xchdir("/proc");
1181
1182#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1183        sort_function[0] = pcpu_sort;
1184        sort_function[1] = mem_sort;
1185        sort_function[2] = time_sort;
1186#else
1187        sort_function[0] = mem_sort;
1188#endif
1189
1190        if (OPT_BATCH_MODE) {
1191                option_mask32 |= OPT_EOF;
1192        }
1193#if ENABLE_FEATURE_TOP_INTERACTIVE
1194        else {
1195                /* Turn on unbuffered input; turn off echoing, ^C ^Z etc */
1196                set_termios_to_raw(STDIN_FILENO, &initial_settings, TERMIOS_CLEAR_ISIG);
1197                die_func = reset_term;
1198        }
1199
1200        bb_signals(BB_FATAL_SIGS, sig_catcher);
1201
1202        /* Eat initial input, if any */
1203        scan_mask = handle_input(scan_mask, 0);
1204#endif
1205
1206        while (scan_mask != EXIT_MASK) {
1207                IF_FEATURE_TOP_INTERACTIVE(unsigned new_mask;)
1208                procps_status_t *p = NULL;
1209
1210                if (OPT_BATCH_MODE) {
1211                        G.lines = INT_MAX;
1212                        col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
1213                } else {
1214                        G.lines = 24; /* default */
1215                        col = 79;
1216                        /* We output to stdout, we need size of stdout (not stdin)! */
1217                        get_terminal_width_height(STDOUT_FILENO, &col, &G.lines);
1218                        if (G.lines < 5 || col < 10) {
1219                                sleep_for_duration(interval);
1220                                continue;
1221                        }
1222                        if (col > LINE_BUF_SIZE - 2)
1223                                col = LINE_BUF_SIZE - 2;
1224                }
1225
1226                /* read process IDs & status for all the processes */
1227                ntop = 0;
1228                while ((p = procps_scan(p, scan_mask)) != NULL) {
1229                        int n;
1230
1231                        IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1232                                n = ntop;
1233                                top = xrealloc_vector(top, 6, ntop++);
1234                                top[n].pid = p->pid;
1235                                top[n].ppid = p->ppid;
1236                                top[n].vsz = p->vsz;
1237#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1238                                top[n].ticks = p->stime + p->utime;
1239#endif
1240                                top[n].uid = p->uid;
1241                                strcpy(top[n].state, p->state);
1242                                strcpy(top[n].comm, p->comm);
1243#if ENABLE_FEATURE_TOP_SMP_PROCESS
1244                                top[n].last_seen_on_cpu = p->last_seen_on_cpu;
1245#endif
1246                        }
1247#if ENABLE_FEATURE_TOPMEM
1248                        else { /* TOPMEM */
1249                                if (!(p->smaps.mapped_ro | p->smaps.mapped_rw))
1250                                        continue; /* kernel threads are ignored */
1251                                n = ntop;
1252                                /* No bug here - top and topmem are the same */
1253                                top = xrealloc_vector(topmem, 6, ntop++);
1254                                strcpy(topmem[n].comm, p->comm);
1255                                topmem[n].pid      = p->pid;
1256                                topmem[n].vsz      = p->smaps.mapped_rw + p->smaps.mapped_ro;
1257                                topmem[n].vszrw    = p->smaps.mapped_rw;
1258                                topmem[n].rss_sh   = p->smaps.shared_clean + p->smaps.shared_dirty;
1259                                topmem[n].rss      = p->smaps.private_clean + p->smaps.private_dirty + topmem[n].rss_sh;
1260                                topmem[n].dirty    = p->smaps.private_dirty + p->smaps.shared_dirty;
1261                                topmem[n].dirty_sh = p->smaps.shared_dirty;
1262                                topmem[n].stack    = p->smaps.stack;
1263                        }
1264#endif
1265                } /* end of "while we read /proc" */
1266                if (ntop == 0) {
1267                        bb_simple_error_msg("no process info in /proc");
1268                        break;
1269                }
1270
1271                IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1272#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1273                        if (!prev_hist_count) {
1274                                do_stats();
1275                                usleep(100000);
1276                                clearmems();
1277                                continue;
1278                        }
1279                        do_stats();
1280                        /* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
1281                        qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
1282#else
1283                        qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
1284#endif
1285                }
1286#if ENABLE_FEATURE_TOPMEM
1287                else { /* TOPMEM */
1288                        qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
1289                }
1290#endif
1291 IF_FEATURE_TOP_INTERACTIVE(display:)
1292                IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
1293                        display_process_list(G.lines, col);
1294                }
1295#if ENABLE_FEATURE_TOPMEM
1296                else { /* TOPMEM */
1297                        display_topmem_process_list(G.lines, col);
1298                }
1299#endif
1300                if (iterations >= 0 && !--iterations)
1301                        break;
1302#if !ENABLE_FEATURE_TOP_INTERACTIVE
1303                clearmems();
1304                sleep_for_duration(interval);
1305#else
1306                new_mask = handle_input(scan_mask, interval);
1307                if (new_mask == NO_RESCAN_MASK)
1308                        goto display;
1309                scan_mask = new_mask;
1310                clearmems();
1311#endif
1312        } /* end of "while (not Q)" */
1313
1314        bb_putchar('\n');
1315#if ENABLE_FEATURE_TOP_INTERACTIVE
1316        reset_term();
1317#endif
1318        if (ENABLE_FEATURE_CLEAN_UP) {
1319                clearmems();
1320#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
1321                free(prev_hist);
1322#endif
1323        }
1324        return EXIT_SUCCESS;
1325}
1326