busybox/procps/nmeter.c
<<
>>
Prefs
   1/*
   2 * Licensed under GPLv2, see file LICENSE in this source tree.
   3 *
   4 * Based on nanotop.c from floppyfw project
   5 *
   6 * Contact me: vda.linux@googlemail.com
   7 */
   8
   9//config:config NMETER
  10//config:       bool "nmeter"
  11//config:       default y
  12//config:       help
  13//config:         Prints selected system stats continuously, one line per update.
  14
  15//applet:IF_NMETER(APPLET(nmeter, BB_DIR_USR_BIN, BB_SUID_DROP))
  16
  17//kbuild:lib-$(CONFIG_NMETER) += nmeter.o
  18
  19//usage:#define nmeter_trivial_usage
  20//usage:       "[-d MSEC] FORMAT_STRING"
  21//usage:#define nmeter_full_usage "\n\n"
  22//usage:       "Monitor system in real time"
  23//usage:     "\n"
  24//usage:     "\n -d MSEC        Milliseconds between updates (default:1000)"
  25//usage:     "\n"
  26//usage:     "\nFormat specifiers:"
  27//usage:     "\n %Nc or %[cN]   CPU. N - bar size (default:10)"
  28//usage:     "\n                (displays: S:system U:user N:niced D:iowait I:irq i:softirq)"
  29//usage:     "\n %[nINTERFACE]  Network INTERFACE"
  30//usage:     "\n %m             Allocated memory"
  31//usage:     "\n %[mf]          Free memory"
  32//usage:     "\n %[mt]          Total memory"
  33//usage:     "\n %s             Allocated swap"
  34//usage:     "\n %f             Number of used file descriptors"
  35//usage:     "\n %Ni            Total/specific IRQ rate"
  36//usage:     "\n %x             Context switch rate"
  37//usage:     "\n %p             Forks"
  38//usage:     "\n %[pn]          # of processes"
  39//usage:     "\n %b             Block io"
  40//usage:     "\n %Nt            Time (with N decimal points)"
  41//usage:     "\n %r             Print <cr> instead of <lf> at EOL"
  42
  43//TODO:
  44// simplify code
  45// /proc/locks
  46// /proc/stat:
  47// disk_io: (3,0):(22272,17897,410702,4375,54750)
  48// btime 1059401962
  49//TODO: use sysinfo libc call/syscall, if appropriate
  50// (faster than open/read/close):
  51// sysinfo({uptime=15017, loads=[5728, 15040, 16480]
  52//  totalram=2107416576, freeram=211525632, sharedram=0, bufferram=157204480}
  53//  totalswap=134209536, freeswap=134209536, procs=157})
  54
  55#include "libbb.h"
  56
  57typedef unsigned long long ullong;
  58
  59enum {  /* Preferably use powers of 2 */
  60        PROC_MIN_FILE_SIZE = 256,
  61        PROC_MAX_FILE_SIZE = 16 * 1024,
  62};
  63
  64typedef struct proc_file {
  65        char *file;
  66        int file_sz;
  67        smallint last_gen;
  68} proc_file;
  69
  70static const char *const proc_name[] = {
  71        "stat",         // Must match the order of proc_file's!
  72        "loadavg",
  73        "net/dev",
  74        "meminfo",
  75        "diskstats",
  76        "sys/fs/file-nr"
  77};
  78
  79struct globals {
  80        // Sample generation flip-flop
  81        smallint gen;
  82        // Linux 2.6? (otherwise assumes 2.4)
  83        smallint is26;
  84        // 1 if sample delay is not an integer fraction of a second
  85        smallint need_seconds;
  86        char *cur_outbuf;
  87        const char *final_str;
  88        int delta;
  89        int deltanz;
  90        struct timeval tv;
  91#define first_proc_file proc_stat
  92        proc_file proc_stat;    // Must match the order of proc_name's!
  93        proc_file proc_loadavg;
  94        proc_file proc_net_dev;
  95        proc_file proc_meminfo;
  96        proc_file proc_diskstats;
  97        proc_file proc_sys_fs_filenr;
  98};
  99#define G (*ptr_to_globals)
 100#define gen                (G.gen               )
 101#define is26               (G.is26              )
 102#define need_seconds       (G.need_seconds      )
 103#define cur_outbuf         (G.cur_outbuf        )
 104#define final_str          (G.final_str         )
 105#define delta              (G.delta             )
 106#define deltanz            (G.deltanz           )
 107#define tv                 (G.tv                )
 108#define proc_stat          (G.proc_stat         )
 109#define proc_loadavg       (G.proc_loadavg      )
 110#define proc_net_dev       (G.proc_net_dev      )
 111#define proc_meminfo       (G.proc_meminfo      )
 112#define proc_diskstats     (G.proc_diskstats    )
 113#define proc_sys_fs_filenr (G.proc_sys_fs_filenr)
 114#define INIT_G() do { \
 115        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
 116        cur_outbuf = outbuf; \
 117        final_str = "\n"; \
 118        deltanz = delta = 1000000; \
 119} while (0)
 120
 121// We depend on this being a char[], not char* - we take sizeof() of it
 122#define outbuf bb_common_bufsiz1
 123
 124static inline void reset_outbuf(void)
 125{
 126        cur_outbuf = outbuf;
 127}
 128
 129static inline int outbuf_count(void)
 130{
 131        return cur_outbuf - outbuf;
 132}
 133
 134static void print_outbuf(void)
 135{
 136        int sz = cur_outbuf - outbuf;
 137        if (sz > 0) {
 138                xwrite(STDOUT_FILENO, outbuf, sz);
 139                cur_outbuf = outbuf;
 140        }
 141}
 142
 143static void put(const char *s)
 144{
 145        int sz = strlen(s);
 146        if (sz > outbuf + sizeof(outbuf) - cur_outbuf)
 147                sz = outbuf + sizeof(outbuf) - cur_outbuf;
 148        memcpy(cur_outbuf, s, sz);
 149        cur_outbuf += sz;
 150}
 151
 152static void put_c(char c)
 153{
 154        if (cur_outbuf < outbuf + sizeof(outbuf))
 155                *cur_outbuf++ = c;
 156}
 157
 158static void put_question_marks(int count)
 159{
 160        while (count--)
 161                put_c('?');
 162}
 163
 164static void readfile_z(proc_file *pf, const char* fname)
 165{
 166// open_read_close() will do two reads in order to be sure we are at EOF,
 167// and we don't need/want that.
 168        int fd;
 169        int sz, rdsz;
 170        char *buf;
 171
 172        sz = pf->file_sz;
 173        buf = pf->file;
 174        if (!buf) {
 175                buf = xmalloc(PROC_MIN_FILE_SIZE);
 176                sz = PROC_MIN_FILE_SIZE;
 177        }
 178 again:
 179        fd = xopen(fname, O_RDONLY);
 180        buf[0] = '\0';
 181        rdsz = read(fd, buf, sz-1);
 182        close(fd);
 183        if (rdsz > 0) {
 184                if (rdsz == sz-1 && sz < PROC_MAX_FILE_SIZE) {
 185                        sz *= 2;
 186                        buf = xrealloc(buf, sz);
 187                        goto again;
 188                }
 189                buf[rdsz] = '\0';
 190        }
 191        pf->file_sz = sz;
 192        pf->file = buf;
 193}
 194
 195static const char* get_file(proc_file *pf)
 196{
 197        if (pf->last_gen != gen) {
 198                pf->last_gen = gen;
 199                readfile_z(pf, proc_name[pf - &first_proc_file]);
 200        }
 201        return pf->file;
 202}
 203
 204static ullong read_after_slash(const char *p)
 205{
 206        p = strchr(p, '/');
 207        if (!p) return 0;
 208        return strtoull(p+1, NULL, 10);
 209}
 210
 211enum conv_type { conv_decimal, conv_slash };
 212
 213// Reads decimal values from line. Values start after key, for example:
 214// "cpu  649369 0 341297 4336769..." - key is "cpu" here.
 215// Values are stored in vec[]. arg_ptr has list of positions
 216// we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value.
 217static int vrdval(const char* p, const char* key,
 218        enum conv_type conv, ullong *vec, va_list arg_ptr)
 219{
 220        int indexline;
 221        int indexnext;
 222
 223        p = strstr(p, key);
 224        if (!p) return 1;
 225
 226        p += strlen(key);
 227        indexline = 1;
 228        indexnext = va_arg(arg_ptr, int);
 229        while (1) {
 230                while (*p == ' ' || *p == '\t') p++;
 231                if (*p == '\n' || *p == '\0') break;
 232
 233                if (indexline == indexnext) { // read this value
 234                        *vec++ = conv==conv_decimal ?
 235                                strtoull(p, NULL, 10) :
 236                                read_after_slash(p);
 237                        indexnext = va_arg(arg_ptr, int);
 238                }
 239                while (*p > ' ') p++; // skip over value
 240                indexline++;
 241        }
 242        return 0;
 243}
 244
 245// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0":
 246// rdval(file_contents, "string_to_find", result_vector, value#, value#...)
 247// value# start with 1
 248static int rdval(const char* p, const char* key, ullong *vec, ...)
 249{
 250        va_list arg_ptr;
 251        int result;
 252
 253        va_start(arg_ptr, vec);
 254        result = vrdval(p, key, conv_decimal, vec, arg_ptr);
 255        va_end(arg_ptr);
 256
 257        return result;
 258}
 259
 260// Parses files with lines like "... ... ... 3/148 ...."
 261static int rdval_loadavg(const char* p, ullong *vec, ...)
 262{
 263        va_list arg_ptr;
 264        int result;
 265
 266        va_start(arg_ptr, vec);
 267        result = vrdval(p, "", conv_slash, vec, arg_ptr);
 268        va_end(arg_ptr);
 269
 270        return result;
 271}
 272
 273// Parses /proc/diskstats
 274//   1  2 3   4  5        6(rd)  7      8     9     10(wr) 11     12 13     14
 275//   3  0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933
 276//   3  1 hda1 0 0 0 0 <- ignore if only 4 fields
 277static int rdval_diskstats(const char* p, ullong *vec)
 278{
 279        ullong rd = rd; // for compiler
 280        int indexline = 0;
 281        vec[0] = 0;
 282        vec[1] = 0;
 283        while (1) {
 284                indexline++;
 285                while (*p == ' ' || *p == '\t') p++;
 286                if (*p == '\0') break;
 287                if (*p == '\n') {
 288                        indexline = 0;
 289                        p++;
 290                        continue;
 291                }
 292                if (indexline == 6) {
 293                        rd = strtoull(p, NULL, 10);
 294                } else if (indexline == 10) {
 295                        vec[0] += rd;  // TODO: *sectorsize (don't know how to find out sectorsize)
 296                        vec[1] += strtoull(p, NULL, 10);
 297                        while (*p != '\n' && *p != '\0') p++;
 298                        continue;
 299                }
 300                while (*p > ' ') p++; // skip over value
 301        }
 302        return 0;
 303}
 304
 305static void scale(ullong ul)
 306{
 307        char buf[5];
 308
 309        /* see http://en.wikipedia.org/wiki/Tera */
 310        smart_ulltoa4(ul, buf, " kmgtpezy");
 311        buf[4] = '\0';
 312        put(buf);
 313}
 314
 315
 316#define S_STAT(a) \
 317typedef struct a { \
 318        struct s_stat *next; \
 319        void (*collect)(struct a *s) FAST_FUNC; \
 320        const char *label;
 321#define S_STAT_END(a) } a;
 322
 323S_STAT(s_stat)
 324S_STAT_END(s_stat)
 325
 326static void FAST_FUNC collect_literal(s_stat *s UNUSED_PARAM)
 327{
 328}
 329
 330static s_stat* init_literal(void)
 331{
 332        s_stat *s = xzalloc(sizeof(*s));
 333        s->collect = collect_literal;
 334        return (s_stat*)s;
 335}
 336
 337static s_stat* init_delay(const char *param)
 338{
 339        delta = strtoul(param, NULL, 0) * 1000; /* param can be "" */
 340        deltanz = delta > 0 ? delta : 1;
 341        need_seconds = (1000000%deltanz) != 0;
 342        return NULL;
 343}
 344
 345static s_stat* init_cr(const char *param UNUSED_PARAM)
 346{
 347        final_str = "\r";
 348        return (s_stat*)0;
 349}
 350
 351
 352//     user nice system idle  iowait irq  softirq (last 3 only in 2.6)
 353//cpu  649369 0 341297 4336769 11640 7122 1183
 354//cpuN 649369 0 341297 4336769 11640 7122 1183
 355enum { CPU_FIELDCNT = 7 };
 356S_STAT(cpu_stat)
 357        ullong old[CPU_FIELDCNT];
 358        int bar_sz;
 359        char *bar;
 360S_STAT_END(cpu_stat)
 361
 362
 363static void FAST_FUNC collect_cpu(cpu_stat *s)
 364{
 365        ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
 366        unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
 367        ullong all = 0;
 368        int norm_all = 0;
 369        int bar_sz = s->bar_sz;
 370        char *bar = s->bar;
 371        int i;
 372
 373        if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) {
 374                put_question_marks(bar_sz);
 375                return;
 376        }
 377
 378        for (i = 0; i < CPU_FIELDCNT; i++) {
 379                ullong old = s->old[i];
 380                if (data[i] < old) old = data[i];               //sanitize
 381                s->old[i] = data[i];
 382                all += (data[i] -= old);
 383        }
 384
 385        if (all) {
 386                for (i = 0; i < CPU_FIELDCNT; i++) {
 387                        ullong t = bar_sz * data[i];
 388                        norm_all += data[i] = t / all;
 389                        frac[i] = t % all;
 390                }
 391
 392                while (norm_all < bar_sz) {
 393                        unsigned max = frac[0];
 394                        int pos = 0;
 395                        for (i = 1; i < CPU_FIELDCNT; i++) {
 396                                if (frac[i] > max) max = frac[i], pos = i;
 397                        }
 398                        frac[pos] = 0;  //avoid bumping up same value twice
 399                        data[pos]++;
 400                        norm_all++;
 401                }
 402
 403                memset(bar, '.', bar_sz);
 404                memset(bar, 'S', data[2]); bar += data[2]; //sys
 405                memset(bar, 'U', data[0]); bar += data[0]; //usr
 406                memset(bar, 'N', data[1]); bar += data[1]; //nice
 407                memset(bar, 'D', data[4]); bar += data[4]; //iowait
 408                memset(bar, 'I', data[5]); bar += data[5]; //irq
 409                memset(bar, 'i', data[6]); bar += data[6]; //softirq
 410        } else {
 411                memset(bar, '?', bar_sz);
 412        }
 413        put(s->bar);
 414}
 415
 416
 417static s_stat* init_cpu(const char *param)
 418{
 419        int sz;
 420        cpu_stat *s = xzalloc(sizeof(*s));
 421        s->collect = collect_cpu;
 422        sz = strtoul(param, NULL, 0); /* param can be "" */
 423        if (sz < 10) sz = 10;
 424        if (sz > 1000) sz = 1000;
 425        s->bar = xzalloc(sz+1);
 426        /*s->bar[sz] = '\0'; - xzalloc did it */
 427        s->bar_sz = sz;
 428        return (s_stat*)s;
 429}
 430
 431
 432S_STAT(int_stat)
 433        ullong old;
 434        int no;
 435S_STAT_END(int_stat)
 436
 437static void FAST_FUNC collect_int(int_stat *s)
 438{
 439        ullong data[1];
 440        ullong old;
 441
 442        if (rdval(get_file(&proc_stat), "intr", data, s->no)) {
 443                put_question_marks(4);
 444                return;
 445        }
 446
 447        old = s->old;
 448        if (data[0] < old) old = data[0];               //sanitize
 449        s->old = data[0];
 450        scale(data[0] - old);
 451}
 452
 453static s_stat* init_int(const char *param)
 454{
 455        int_stat *s = xzalloc(sizeof(*s));
 456        s->collect = collect_int;
 457        if (param[0] == '\0') {
 458                s->no = 1;
 459        } else {
 460                int n = xatoi_positive(param);
 461                s->no = n + 2;
 462        }
 463        return (s_stat*)s;
 464}
 465
 466
 467S_STAT(ctx_stat)
 468        ullong old;
 469S_STAT_END(ctx_stat)
 470
 471static void FAST_FUNC collect_ctx(ctx_stat *s)
 472{
 473        ullong data[1];
 474        ullong old;
 475
 476        if (rdval(get_file(&proc_stat), "ctxt", data, 1)) {
 477                put_question_marks(4);
 478                return;
 479        }
 480
 481        old = s->old;
 482        if (data[0] < old) old = data[0];               //sanitize
 483        s->old = data[0];
 484        scale(data[0] - old);
 485}
 486
 487static s_stat* init_ctx(const char *param UNUSED_PARAM)
 488{
 489        ctx_stat *s = xzalloc(sizeof(*s));
 490        s->collect = collect_ctx;
 491        return (s_stat*)s;
 492}
 493
 494
 495S_STAT(blk_stat)
 496        const char* lookfor;
 497        ullong old[2];
 498S_STAT_END(blk_stat)
 499
 500static void FAST_FUNC collect_blk(blk_stat *s)
 501{
 502        ullong data[2];
 503        int i;
 504
 505        if (is26) {
 506                i = rdval_diskstats(get_file(&proc_diskstats), data);
 507        } else {
 508                i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2);
 509                // Linux 2.4 reports bio in Kbytes, convert to sectors:
 510                data[0] *= 2;
 511                data[1] *= 2;
 512        }
 513        if (i) {
 514                put_question_marks(9);
 515                return;
 516        }
 517
 518        for (i=0; i<2; i++) {
 519                ullong old = s->old[i];
 520                if (data[i] < old) old = data[i];               //sanitize
 521                s->old[i] = data[i];
 522                data[i] -= old;
 523        }
 524        scale(data[0]*512); // TODO: *sectorsize
 525        put_c(' ');
 526        scale(data[1]*512);
 527}
 528
 529static s_stat* init_blk(const char *param UNUSED_PARAM)
 530{
 531        blk_stat *s = xzalloc(sizeof(*s));
 532        s->collect = collect_blk;
 533        s->lookfor = "page";
 534        return (s_stat*)s;
 535}
 536
 537
 538S_STAT(fork_stat)
 539        ullong old;
 540S_STAT_END(fork_stat)
 541
 542static void FAST_FUNC collect_thread_nr(fork_stat *s UNUSED_PARAM)
 543{
 544        ullong data[1];
 545
 546        if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) {
 547                put_question_marks(4);
 548                return;
 549        }
 550        scale(data[0]);
 551}
 552
 553static void FAST_FUNC collect_fork(fork_stat *s)
 554{
 555        ullong data[1];
 556        ullong old;
 557
 558        if (rdval(get_file(&proc_stat), "processes", data, 1)) {
 559                put_question_marks(4);
 560                return;
 561        }
 562
 563        old = s->old;
 564        if (data[0] < old) old = data[0];       //sanitize
 565        s->old = data[0];
 566        scale(data[0] - old);
 567}
 568
 569static s_stat* init_fork(const char *param)
 570{
 571        fork_stat *s = xzalloc(sizeof(*s));
 572        if (*param == 'n') {
 573                s->collect = collect_thread_nr;
 574        } else {
 575                s->collect = collect_fork;
 576        }
 577        return (s_stat*)s;
 578}
 579
 580
 581S_STAT(if_stat)
 582        ullong old[4];
 583        const char *device;
 584        char *device_colon;
 585S_STAT_END(if_stat)
 586
 587static void FAST_FUNC collect_if(if_stat *s)
 588{
 589        ullong data[4];
 590        int i;
 591
 592        if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) {
 593                put_question_marks(10);
 594                return;
 595        }
 596
 597        for (i=0; i<4; i++) {
 598                ullong old = s->old[i];
 599                if (data[i] < old) old = data[i];               //sanitize
 600                s->old[i] = data[i];
 601                data[i] -= old;
 602        }
 603        put_c(data[1] ? '*' : ' ');
 604        scale(data[0]);
 605        put_c(data[3] ? '*' : ' ');
 606        scale(data[2]);
 607}
 608
 609static s_stat* init_if(const char *device)
 610{
 611        if_stat *s = xzalloc(sizeof(*s));
 612
 613        if (!device || !device[0])
 614                bb_show_usage();
 615        s->collect = collect_if;
 616
 617        s->device = device;
 618        s->device_colon = xasprintf("%s:", device);
 619        return (s_stat*)s;
 620}
 621
 622
 623S_STAT(mem_stat)
 624        char opt;
 625S_STAT_END(mem_stat)
 626
 627// "Memory" value should not include any caches.
 628// IOW: neither "ls -laR /" nor heavy read/write activity
 629//      should affect it. We'd like to also include any
 630//      long-term allocated kernel-side mem, but it is hard
 631//      to figure out. For now, bufs, cached & slab are
 632//      counted as "free" memory
 633//2.6.16:
 634//MemTotal:       773280 kB
 635//MemFree:         25912 kB - genuinely free
 636//Buffers:        320672 kB - cache
 637//Cached:         146396 kB - cache
 638//SwapCached:          0 kB
 639//Active:         183064 kB
 640//Inactive:       356892 kB
 641//HighTotal:           0 kB
 642//HighFree:            0 kB
 643//LowTotal:       773280 kB
 644//LowFree:         25912 kB
 645//SwapTotal:      131064 kB
 646//SwapFree:       131064 kB
 647//Dirty:              48 kB
 648//Writeback:           0 kB
 649//Mapped:          96620 kB
 650//Slab:           200668 kB - takes 7 Mb on my box fresh after boot,
 651//                            but includes dentries and inodes
 652//                            (== can take arbitrary amount of mem)
 653//CommitLimit:    517704 kB
 654//Committed_AS:   236776 kB
 655//PageTables:       1248 kB
 656//VmallocTotal:   516052 kB
 657//VmallocUsed:      3852 kB
 658//VmallocChunk:   512096 kB
 659//HugePages_Total:     0
 660//HugePages_Free:      0
 661//Hugepagesize:     4096 kB
 662static void FAST_FUNC collect_mem(mem_stat *s)
 663{
 664        ullong m_total = 0;
 665        ullong m_free = 0;
 666        ullong m_bufs = 0;
 667        ullong m_cached = 0;
 668        ullong m_slab = 0;
 669
 670        if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) {
 671                put_question_marks(4);
 672                return;
 673        }
 674        if (s->opt == 't') {
 675                scale(m_total << 10);
 676                return;
 677        }
 678
 679        if (rdval(proc_meminfo.file, "MemFree:", &m_free  , 1)
 680         || rdval(proc_meminfo.file, "Buffers:", &m_bufs  , 1)
 681         || rdval(proc_meminfo.file, "Cached:",  &m_cached, 1)
 682         || rdval(proc_meminfo.file, "Slab:",    &m_slab  , 1)
 683        ) {
 684                put_question_marks(4);
 685                return;
 686        }
 687
 688        m_free += m_bufs + m_cached + m_slab;
 689        switch (s->opt) {
 690        case 'f':
 691                scale(m_free << 10); break;
 692        default:
 693                scale((m_total - m_free) << 10); break;
 694        }
 695}
 696
 697static s_stat* init_mem(const char *param)
 698{
 699        mem_stat *s = xzalloc(sizeof(*s));
 700        s->collect = collect_mem;
 701        s->opt = param[0];
 702        return (s_stat*)s;
 703}
 704
 705
 706S_STAT(swp_stat)
 707S_STAT_END(swp_stat)
 708
 709static void FAST_FUNC collect_swp(swp_stat *s UNUSED_PARAM)
 710{
 711        ullong s_total[1];
 712        ullong s_free[1];
 713        if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1)
 714         || rdval(proc_meminfo.file,       "SwapFree:" , s_free,  1)
 715        ) {
 716                put_question_marks(4);
 717                return;
 718        }
 719        scale((s_total[0]-s_free[0]) << 10);
 720}
 721
 722static s_stat* init_swp(const char *param UNUSED_PARAM)
 723{
 724        swp_stat *s = xzalloc(sizeof(*s));
 725        s->collect = collect_swp;
 726        return (s_stat*)s;
 727}
 728
 729
 730S_STAT(fd_stat)
 731S_STAT_END(fd_stat)
 732
 733static void FAST_FUNC collect_fd(fd_stat *s UNUSED_PARAM)
 734{
 735        ullong data[2];
 736
 737        if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) {
 738                put_question_marks(4);
 739                return;
 740        }
 741
 742        scale(data[0] - data[1]);
 743}
 744
 745static s_stat* init_fd(const char *param UNUSED_PARAM)
 746{
 747        fd_stat *s = xzalloc(sizeof(*s));
 748        s->collect = collect_fd;
 749        return (s_stat*)s;
 750}
 751
 752
 753S_STAT(time_stat)
 754        int prec;
 755        int scale;
 756S_STAT_END(time_stat)
 757
 758static void FAST_FUNC collect_time(time_stat *s)
 759{
 760        char buf[sizeof("12:34:56.123456")];
 761        struct tm* tm;
 762        int us = tv.tv_usec + s->scale/2;
 763        time_t t = tv.tv_sec;
 764
 765        if (us >= 1000000) {
 766                t++;
 767                us -= 1000000;
 768        }
 769        tm = localtime(&t);
 770
 771        sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
 772        if (s->prec)
 773                sprintf(buf+8, ".%0*d", s->prec, us / s->scale);
 774        put(buf);
 775}
 776
 777static s_stat* init_time(const char *param)
 778{
 779        int prec;
 780        time_stat *s = xzalloc(sizeof(*s));
 781
 782        s->collect = collect_time;
 783        prec = param[0] - '0';
 784        if (prec < 0) prec = 0;
 785        else if (prec > 6) prec = 6;
 786        s->prec = prec;
 787        s->scale = 1;
 788        while (prec++ < 6)
 789                s->scale *= 10;
 790        return (s_stat*)s;
 791}
 792
 793static void FAST_FUNC collect_info(s_stat *s)
 794{
 795        gen ^= 1;
 796        while (s) {
 797                put(s->label);
 798                s->collect(s);
 799                s = s->next;
 800        }
 801}
 802
 803
 804typedef s_stat* init_func(const char *param);
 805
 806// Deprecated %NNNd is to be removed, -d MSEC supersedes it
 807static const char options[] ALIGN1 = "ncmsfixptbdr";
 808static init_func *const init_functions[] = {
 809        init_if,
 810        init_cpu,
 811        init_mem,
 812        init_swp,
 813        init_fd,
 814        init_int,
 815        init_ctx,
 816        init_fork,
 817        init_time,
 818        init_blk,
 819        init_delay,
 820        init_cr
 821};
 822
 823int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 824int nmeter_main(int argc UNUSED_PARAM, char **argv)
 825{
 826        char buf[32];
 827        s_stat *first = NULL;
 828        s_stat *last = NULL;
 829        s_stat *s;
 830        char *opt_d;
 831        char *cur, *prev;
 832
 833        INIT_G();
 834
 835        xchdir("/proc");
 836
 837        if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
 838                buf[sizeof(buf)-1] = '\0';
 839                is26 = (strstr(buf, " 2.4.") == NULL);
 840        }
 841
 842        if (getopt32(argv, "d:", &opt_d))
 843                init_delay(opt_d);
 844        argv += optind;
 845
 846        if (!argv[0])
 847                bb_show_usage();
 848
 849        // Can use argv[0] directly, but this will mess up
 850        // parameters as seen by e.g. ps. Making a copy...
 851        cur = xstrdup(argv[0]);
 852        while (1) {
 853                char *param, *p;
 854                prev = cur;
 855 again:
 856                cur = strchr(cur, '%');
 857                if (!cur)
 858                        break;
 859                if (cur[1] == '%') {    // %%
 860                        overlapping_strcpy(cur, cur + 1);
 861                        cur++;
 862                        goto again;
 863                }
 864                *cur++ = '\0';          // overwrite %
 865                if (cur[0] == '[') {
 866                        // format: %[foptstring]
 867                        cur++;
 868                        p = strchr(options, cur[0]);
 869                        param = cur+1;
 870                        while (cur[0] != ']') {
 871                                if (!cur[0])
 872                                        bb_show_usage();
 873                                cur++;
 874                        }
 875                        *cur++ = '\0';  // overwrite [
 876                } else {
 877                        // format: %NNNNNNf
 878                        param = cur;
 879                        while (cur[0] >= '0' && cur[0] <= '9')
 880                                cur++;
 881                        if (!cur[0])
 882                                bb_show_usage();
 883                        p = strchr(options, cur[0]);
 884                        *cur++ = '\0';  // overwrite format char
 885                }
 886                if (!p)
 887                        bb_show_usage();
 888                s = init_functions[p-options](param);
 889                if (s) {
 890                        s->label = prev;
 891                        /*s->next = NULL; - all initXXX funcs use xzalloc */
 892                        if (!first)
 893                                first = s;
 894                        else
 895                                last->next = s;
 896                        last = s;
 897                } else {
 898                        // %NNNNd or %r option. remove it from string
 899                        strcpy(prev + strlen(prev), cur);
 900                        cur = prev;
 901                }
 902        }
 903        if (prev[0]) {
 904                s = init_literal();
 905                s->label = prev;
 906                /*s->next = NULL; - all initXXX funcs use xzalloc */
 907                if (!first)
 908                        first = s;
 909                else
 910                        last->next = s;
 911                last = s;
 912        }
 913
 914        // Generate first samples but do not print them, they're bogus
 915        collect_info(first);
 916        reset_outbuf();
 917        if (delta >= 0) {
 918                gettimeofday(&tv, NULL);
 919                usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz);
 920        }
 921
 922        while (1) {
 923                gettimeofday(&tv, NULL);
 924                collect_info(first);
 925                put(final_str);
 926                print_outbuf();
 927
 928                // Negative delta -> no usleep at all
 929                // This will hog the CPU but you can have REALLY GOOD
 930                // time resolution ;)
 931                // TODO: detect and avoid useless updates
 932                // (like: nothing happens except time)
 933                if (delta >= 0) {
 934                        int rem;
 935                        // can be commented out, will sacrifice sleep time precision a bit
 936                        gettimeofday(&tv, NULL);
 937                        if (need_seconds)
 938                                rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz;
 939                        else
 940                                rem = delta - tv.tv_usec%deltanz;
 941                        // Sometimes kernel wakes us up just a tiny bit earlier than asked
 942                        // Do not go to very short sleep in this case
 943                        if (rem < delta/128) {
 944                                rem += delta;
 945                        }
 946                        usleep(rem);
 947                }
 948        }
 949
 950        /*return 0;*/
 951}
 952