source: branches/2.2.5/mindi-busybox/procps/nmeter.c @ 1765

Last change on this file since 1765 was 1765, checked in by Bruno Cornec, 13 years ago

Update to busybox 1.7.2

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