source: branches/3.2/mindi-busybox/procps/nmeter.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
  • Property svn:eol-style set to native
File size: 21.3 KB
Line 
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
277// Linux 3.0 (maybe earlier) started printing full stats for hda1 too.
278// Had to add code which skips such devices.
279static int rdval_diskstats(const char* p, ullong *vec)
280{
281    char devname[32];
282    unsigned devname_len = 0;
283    int value_idx = 0;
284
285    vec[0] = 0;
286    vec[1] = 0;
287    while (1) {
288        value_idx++;
289        while (*p == ' ' || *p == '\t')
290            p++;
291        if (*p == '\0')
292            break;
293        if (*p == '\n') {
294            value_idx = 0;
295            p++;
296            continue;
297        }
298        if (value_idx == 3) {
299            char *end = strchrnul(p, ' ');
300            /* If this a hda1-like device (same prefix as last one + digit)? */
301            if (devname_len && strncmp(devname, p, devname_len) == 0 && isdigit(p[devname_len])) {
302                p = end;
303                goto skip_line; /* skip entire line */
304            }
305            /* It is not. Remember the name for future checks */
306            devname_len = end - p;
307            if (devname_len > sizeof(devname)-1)
308                devname_len = sizeof(devname)-1;
309            strncpy(devname, p, devname_len);
310            /* devname[devname_len] = '\0'; - not really needed */
311            p = end;
312        } else
313        if (value_idx == 6) {
314            // TODO: *sectorsize (don't know how to find out sectorsize)
315            vec[0] += strtoull(p, NULL, 10);
316        } else
317        if (value_idx == 10) {
318            // TODO: *sectorsize (don't know how to find out sectorsize)
319            vec[1] += strtoull(p, NULL, 10);
320 skip_line:
321            while (*p != '\n' && *p != '\0')
322                p++;
323            continue;
324        }
325        while ((unsigned char)(*p) > ' ') // skip over value
326            p++;
327    }
328    return 0;
329}
330
331static void scale(ullong ul)
332{
333    char buf[5];
334
335    /* see http://en.wikipedia.org/wiki/Tera */
336    smart_ulltoa4(ul, buf, " kmgtpezy");
337    buf[4] = '\0';
338    put(buf);
339}
340
341
342#define S_STAT(a) \
343typedef struct a { \
344    struct s_stat *next; \
345    void (*collect)(struct a *s) FAST_FUNC; \
346    const char *label;
347#define S_STAT_END(a) } a;
348
349S_STAT(s_stat)
350S_STAT_END(s_stat)
351
352static void FAST_FUNC collect_literal(s_stat *s UNUSED_PARAM)
353{
354}
355
356static s_stat* init_literal(void)
357{
358    s_stat *s = xzalloc(sizeof(*s));
359    s->collect = collect_literal;
360    return (s_stat*)s;
361}
362
363static s_stat* init_delay(const char *param)
364{
365    delta = strtoul(param, NULL, 0) * 1000; /* param can be "" */
366    deltanz = delta > 0 ? delta : 1;
367    need_seconds = (1000000%deltanz) != 0;
368    return NULL;
369}
370
371static s_stat* init_cr(const char *param UNUSED_PARAM)
372{
373    final_str = "\r";
374    return (s_stat*)0;
375}
376
377
378//     user nice system idle  iowait irq  softirq (last 3 only in 2.6)
379//cpu  649369 0 341297 4336769 11640 7122 1183
380//cpuN 649369 0 341297 4336769 11640 7122 1183
381enum { CPU_FIELDCNT = 7 };
382S_STAT(cpu_stat)
383    ullong old[CPU_FIELDCNT];
384    int bar_sz;
385    char *bar;
386S_STAT_END(cpu_stat)
387
388
389static void FAST_FUNC collect_cpu(cpu_stat *s)
390{
391    ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
392    unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
393    ullong all = 0;
394    int norm_all = 0;
395    int bar_sz = s->bar_sz;
396    char *bar = s->bar;
397    int i;
398
399    if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) {
400        put_question_marks(bar_sz);
401        return;
402    }
403
404    for (i = 0; i < CPU_FIELDCNT; i++) {
405        ullong old = s->old[i];
406        if (data[i] < old) old = data[i];       //sanitize
407        s->old[i] = data[i];
408        all += (data[i] -= old);
409    }
410
411    if (all) {
412        for (i = 0; i < CPU_FIELDCNT; i++) {
413            ullong t = bar_sz * data[i];
414            norm_all += data[i] = t / all;
415            frac[i] = t % all;
416        }
417
418        while (norm_all < bar_sz) {
419            unsigned max = frac[0];
420            int pos = 0;
421            for (i = 1; i < CPU_FIELDCNT; i++) {
422                if (frac[i] > max) max = frac[i], pos = i;
423            }
424            frac[pos] = 0;  //avoid bumping up same value twice
425            data[pos]++;
426            norm_all++;
427        }
428
429        memset(bar, '.', bar_sz);
430        memset(bar, 'S', data[2]); bar += data[2]; //sys
431        memset(bar, 'U', data[0]); bar += data[0]; //usr
432        memset(bar, 'N', data[1]); bar += data[1]; //nice
433        memset(bar, 'D', data[4]); bar += data[4]; //iowait
434        memset(bar, 'I', data[5]); bar += data[5]; //irq
435        memset(bar, 'i', data[6]); bar += data[6]; //softirq
436    } else {
437        memset(bar, '?', bar_sz);
438    }
439    put(s->bar);
440}
441
442
443static s_stat* init_cpu(const char *param)
444{
445    int sz;
446    cpu_stat *s = xzalloc(sizeof(*s));
447    s->collect = collect_cpu;
448    sz = strtoul(param, NULL, 0); /* param can be "" */
449    if (sz < 10) sz = 10;
450    if (sz > 1000) sz = 1000;
451    s->bar = xzalloc(sz+1);
452    /*s->bar[sz] = '\0'; - xzalloc did it */
453    s->bar_sz = sz;
454    return (s_stat*)s;
455}
456
457
458S_STAT(int_stat)
459    ullong old;
460    int no;
461S_STAT_END(int_stat)
462
463static void FAST_FUNC collect_int(int_stat *s)
464{
465    ullong data[1];
466    ullong old;
467
468    if (rdval(get_file(&proc_stat), "intr", data, s->no)) {
469        put_question_marks(4);
470        return;
471    }
472
473    old = s->old;
474    if (data[0] < old) old = data[0];       //sanitize
475    s->old = data[0];
476    scale(data[0] - old);
477}
478
479static s_stat* init_int(const char *param)
480{
481    int_stat *s = xzalloc(sizeof(*s));
482    s->collect = collect_int;
483    if (param[0] == '\0') {
484        s->no = 1;
485    } else {
486        int n = xatoi_positive(param);
487        s->no = n + 2;
488    }
489    return (s_stat*)s;
490}
491
492
493S_STAT(ctx_stat)
494    ullong old;
495S_STAT_END(ctx_stat)
496
497static void FAST_FUNC collect_ctx(ctx_stat *s)
498{
499    ullong data[1];
500    ullong old;
501
502    if (rdval(get_file(&proc_stat), "ctxt", data, 1)) {
503        put_question_marks(4);
504        return;
505    }
506
507    old = s->old;
508    if (data[0] < old) old = data[0];       //sanitize
509    s->old = data[0];
510    scale(data[0] - old);
511}
512
513static s_stat* init_ctx(const char *param UNUSED_PARAM)
514{
515    ctx_stat *s = xzalloc(sizeof(*s));
516    s->collect = collect_ctx;
517    return (s_stat*)s;
518}
519
520
521S_STAT(blk_stat)
522    const char* lookfor;
523    ullong old[2];
524S_STAT_END(blk_stat)
525
526static void FAST_FUNC collect_blk(blk_stat *s)
527{
528    ullong data[2];
529    int i;
530
531    if (is26) {
532        i = rdval_diskstats(get_file(&proc_diskstats), data);
533    } else {
534        i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2);
535        // Linux 2.4 reports bio in Kbytes, convert to sectors:
536        data[0] *= 2;
537        data[1] *= 2;
538    }
539    if (i) {
540        put_question_marks(9);
541        return;
542    }
543
544    for (i=0; i<2; i++) {
545        ullong old = s->old[i];
546        if (data[i] < old) old = data[i];       //sanitize
547        s->old[i] = data[i];
548        data[i] -= old;
549    }
550    scale(data[0]*512); // TODO: *sectorsize
551    put_c(' ');
552    scale(data[1]*512);
553}
554
555static s_stat* init_blk(const char *param UNUSED_PARAM)
556{
557    blk_stat *s = xzalloc(sizeof(*s));
558    s->collect = collect_blk;
559    s->lookfor = "page";
560    return (s_stat*)s;
561}
562
563
564S_STAT(fork_stat)
565    ullong old;
566S_STAT_END(fork_stat)
567
568static void FAST_FUNC collect_thread_nr(fork_stat *s UNUSED_PARAM)
569{
570    ullong data[1];
571
572    if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) {
573        put_question_marks(4);
574        return;
575    }
576    scale(data[0]);
577}
578
579static void FAST_FUNC collect_fork(fork_stat *s)
580{
581    ullong data[1];
582    ullong old;
583
584    if (rdval(get_file(&proc_stat), "processes", data, 1)) {
585        put_question_marks(4);
586        return;
587    }
588
589    old = s->old;
590    if (data[0] < old) old = data[0];   //sanitize
591    s->old = data[0];
592    scale(data[0] - old);
593}
594
595static s_stat* init_fork(const char *param)
596{
597    fork_stat *s = xzalloc(sizeof(*s));
598    if (*param == 'n') {
599        s->collect = collect_thread_nr;
600    } else {
601        s->collect = collect_fork;
602    }
603    return (s_stat*)s;
604}
605
606
607S_STAT(if_stat)
608    ullong old[4];
609    const char *device;
610    char *device_colon;
611S_STAT_END(if_stat)
612
613static void FAST_FUNC collect_if(if_stat *s)
614{
615    ullong data[4];
616    int i;
617
618    if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) {
619        put_question_marks(10);
620        return;
621    }
622
623    for (i=0; i<4; i++) {
624        ullong old = s->old[i];
625        if (data[i] < old) old = data[i];       //sanitize
626        s->old[i] = data[i];
627        data[i] -= old;
628    }
629    put_c(data[1] ? '*' : ' ');
630    scale(data[0]);
631    put_c(data[3] ? '*' : ' ');
632    scale(data[2]);
633}
634
635static s_stat* init_if(const char *device)
636{
637    if_stat *s = xzalloc(sizeof(*s));
638
639    if (!device || !device[0])
640        bb_show_usage();
641    s->collect = collect_if;
642
643    s->device = device;
644    s->device_colon = xasprintf("%s:", device);
645    return (s_stat*)s;
646}
647
648
649S_STAT(mem_stat)
650    char opt;
651S_STAT_END(mem_stat)
652
653// "Memory" value should not include any caches.
654// IOW: neither "ls -laR /" nor heavy read/write activity
655//      should affect it. We'd like to also include any
656//      long-term allocated kernel-side mem, but it is hard
657//      to figure out. For now, bufs, cached & slab are
658//      counted as "free" memory
659//2.6.16:
660//MemTotal:       773280 kB
661//MemFree:         25912 kB - genuinely free
662//Buffers:        320672 kB - cache
663//Cached:         146396 kB - cache
664//SwapCached:          0 kB
665//Active:         183064 kB
666//Inactive:       356892 kB
667//HighTotal:           0 kB
668//HighFree:            0 kB
669//LowTotal:       773280 kB
670//LowFree:         25912 kB
671//SwapTotal:      131064 kB
672//SwapFree:       131064 kB
673//Dirty:              48 kB
674//Writeback:           0 kB
675//Mapped:          96620 kB
676//Slab:           200668 kB - takes 7 Mb on my box fresh after boot,
677//                            but includes dentries and inodes
678//                            (== can take arbitrary amount of mem)
679//CommitLimit:    517704 kB
680//Committed_AS:   236776 kB
681//PageTables:       1248 kB
682//VmallocTotal:   516052 kB
683//VmallocUsed:      3852 kB
684//VmallocChunk:   512096 kB
685//HugePages_Total:     0
686//HugePages_Free:      0
687//Hugepagesize:     4096 kB
688static void FAST_FUNC collect_mem(mem_stat *s)
689{
690    ullong m_total = 0;
691    ullong m_free = 0;
692    ullong m_bufs = 0;
693    ullong m_cached = 0;
694    ullong m_slab = 0;
695
696    if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) {
697        put_question_marks(4);
698        return;
699    }
700    if (s->opt == 't') {
701        scale(m_total << 10);
702        return;
703    }
704
705    if (rdval(proc_meminfo.file, "MemFree:", &m_free  , 1)
706     || rdval(proc_meminfo.file, "Buffers:", &m_bufs  , 1)
707     || rdval(proc_meminfo.file, "Cached:",  &m_cached, 1)
708     || rdval(proc_meminfo.file, "Slab:",    &m_slab  , 1)
709    ) {
710        put_question_marks(4);
711        return;
712    }
713
714    m_free += m_bufs + m_cached + m_slab;
715    switch (s->opt) {
716    case 'f':
717        scale(m_free << 10); break;
718    default:
719        scale((m_total - m_free) << 10); break;
720    }
721}
722
723static s_stat* init_mem(const char *param)
724{
725    mem_stat *s = xzalloc(sizeof(*s));
726    s->collect = collect_mem;
727    s->opt = param[0];
728    return (s_stat*)s;
729}
730
731
732S_STAT(swp_stat)
733S_STAT_END(swp_stat)
734
735static void FAST_FUNC collect_swp(swp_stat *s UNUSED_PARAM)
736{
737    ullong s_total[1];
738    ullong s_free[1];
739    if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1)
740     || rdval(proc_meminfo.file,       "SwapFree:" , s_free,  1)
741    ) {
742        put_question_marks(4);
743        return;
744    }
745    scale((s_total[0]-s_free[0]) << 10);
746}
747
748static s_stat* init_swp(const char *param UNUSED_PARAM)
749{
750    swp_stat *s = xzalloc(sizeof(*s));
751    s->collect = collect_swp;
752    return (s_stat*)s;
753}
754
755
756S_STAT(fd_stat)
757S_STAT_END(fd_stat)
758
759static void FAST_FUNC collect_fd(fd_stat *s UNUSED_PARAM)
760{
761    ullong data[2];
762
763    if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) {
764        put_question_marks(4);
765        return;
766    }
767
768    scale(data[0] - data[1]);
769}
770
771static s_stat* init_fd(const char *param UNUSED_PARAM)
772{
773    fd_stat *s = xzalloc(sizeof(*s));
774    s->collect = collect_fd;
775    return (s_stat*)s;
776}
777
778
779S_STAT(time_stat)
780    int prec;
781    int scale;
782S_STAT_END(time_stat)
783
784static void FAST_FUNC collect_time(time_stat *s)
785{
786    char buf[sizeof("12:34:56.123456")];
787    struct tm* tm;
788    int us = tv.tv_usec + s->scale/2;
789    time_t t = tv.tv_sec;
790
791    if (us >= 1000000) {
792        t++;
793        us -= 1000000;
794    }
795    tm = localtime(&t);
796
797    sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
798    if (s->prec)
799        sprintf(buf+8, ".%0*d", s->prec, us / s->scale);
800    put(buf);
801}
802
803static s_stat* init_time(const char *param)
804{
805    int prec;
806    time_stat *s = xzalloc(sizeof(*s));
807
808    s->collect = collect_time;
809    prec = param[0] - '0';
810    if (prec < 0) prec = 0;
811    else if (prec > 6) prec = 6;
812    s->prec = prec;
813    s->scale = 1;
814    while (prec++ < 6)
815        s->scale *= 10;
816    return (s_stat*)s;
817}
818
819static void FAST_FUNC collect_info(s_stat *s)
820{
821    gen ^= 1;
822    while (s) {
823        put(s->label);
824        s->collect(s);
825        s = s->next;
826    }
827}
828
829
830typedef s_stat* init_func(const char *param);
831
832// Deprecated %NNNd is to be removed, -d MSEC supersedes it
833static const char options[] ALIGN1 = "ncmsfixptbdr";
834static init_func *const init_functions[] = {
835    init_if,
836    init_cpu,
837    init_mem,
838    init_swp,
839    init_fd,
840    init_int,
841    init_ctx,
842    init_fork,
843    init_time,
844    init_blk,
845    init_delay,
846    init_cr
847};
848
849int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
850int nmeter_main(int argc UNUSED_PARAM, char **argv)
851{
852    char buf[32];
853    s_stat *first = NULL;
854    s_stat *last = NULL;
855    s_stat *s;
856    char *opt_d;
857    char *cur, *prev;
858
859    INIT_G();
860
861    xchdir("/proc");
862
863    if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
864        buf[sizeof(buf)-1] = '\0';
865        is26 = (strstr(buf, " 2.4.") == NULL);
866    }
867
868    if (getopt32(argv, "d:", &opt_d))
869        init_delay(opt_d);
870    argv += optind;
871
872    if (!argv[0])
873        bb_show_usage();
874
875    // Can use argv[0] directly, but this will mess up
876    // parameters as seen by e.g. ps. Making a copy...
877    cur = xstrdup(argv[0]);
878    while (1) {
879        char *param, *p;
880        prev = cur;
881 again:
882        cur = strchr(cur, '%');
883        if (!cur)
884            break;
885        if (cur[1] == '%') {    // %%
886            overlapping_strcpy(cur, cur + 1);
887            cur++;
888            goto again;
889        }
890        *cur++ = '\0';      // overwrite %
891        if (cur[0] == '[') {
892            // format: %[foptstring]
893            cur++;
894            p = strchr(options, cur[0]);
895            param = cur+1;
896            while (cur[0] != ']') {
897                if (!cur[0])
898                    bb_show_usage();
899                cur++;
900            }
901            *cur++ = '\0';  // overwrite [
902        } else {
903            // format: %NNNNNNf
904            param = cur;
905            while (cur[0] >= '0' && cur[0] <= '9')
906                cur++;
907            if (!cur[0])
908                bb_show_usage();
909            p = strchr(options, cur[0]);
910            *cur++ = '\0';  // overwrite format char
911        }
912        if (!p)
913            bb_show_usage();
914        s = init_functions[p-options](param);
915        if (s) {
916            s->label = prev;
917            /*s->next = NULL; - all initXXX funcs use xzalloc */
918            if (!first)
919                first = s;
920            else
921                last->next = s;
922            last = s;
923        } else {
924            // %NNNNd or %r option. remove it from string
925            strcpy(prev + strlen(prev), cur);
926            cur = prev;
927        }
928    }
929    if (prev[0]) {
930        s = init_literal();
931        s->label = prev;
932        /*s->next = NULL; - all initXXX funcs use xzalloc */
933        if (!first)
934            first = s;
935        else
936            last->next = s;
937        last = s;
938    }
939
940    // Generate first samples but do not print them, they're bogus
941    collect_info(first);
942    reset_outbuf();
943    if (delta >= 0) {
944        gettimeofday(&tv, NULL);
945        usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz);
946    }
947
948    while (1) {
949        gettimeofday(&tv, NULL);
950        collect_info(first);
951        put(final_str);
952        print_outbuf();
953
954        // Negative delta -> no usleep at all
955        // This will hog the CPU but you can have REALLY GOOD
956        // time resolution ;)
957        // TODO: detect and avoid useless updates
958        // (like: nothing happens except time)
959        if (delta >= 0) {
960            int rem;
961            // can be commented out, will sacrifice sleep time precision a bit
962            gettimeofday(&tv, NULL);
963            if (need_seconds)
964                rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz;
965            else
966                rem = delta - tv.tv_usec%deltanz;
967            // Sometimes kernel wakes us up just a tiny bit earlier than asked
968            // Do not go to very short sleep in this case
969            if (rem < delta/128) {
970                rem += delta;
971            }
972            usleep(rem);
973        }
974    }
975
976    /*return 0;*/
977}
Note: See TracBrowser for help on using the repository browser.