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