trylink: produce even more info about final link stage
[platform/upstream/busybox.git] / procps / nmeter.c
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
23 typedef unsigned long long ullong;
24
25 enum { PROC_FILE_SIZE = 4096 };
26
27 typedef struct proc_file {
28         char *file;
29         //const char *name;
30         smallint last_gen;
31 } proc_file;
32
33 static 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
42 struct 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
87 static inline void reset_outbuf(void)
88 {
89         cur_outbuf = outbuf;
90 }
91
92 static inline int outbuf_count(void)
93 {
94         return cur_outbuf - outbuf;
95 }
96
97 static 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
106 static 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
115 static void put_c(char c)
116 {
117         if (cur_outbuf < outbuf + sizeof(outbuf))
118                 *cur_outbuf++ = c;
119 }
120
121 static void put_question_marks(int count)
122 {
123         while (count--)
124                 put_c('?');
125 }
126
127 static 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
142 static 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
156 static 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
163 enum 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.
169 static 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
200 static 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 ...."
213 static 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
229 static 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
257 static 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) \
266 typedef 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
272 S_STAT(s_stat)
273 S_STAT_END(s_stat)
274
275 static void collect_literal(s_stat *s)
276 {
277 }
278
279 static 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
286 static 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
294 static 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
304 enum { CPU_FIELDCNT = 7 };
305 S_STAT(cpu_stat)
306         ullong old[CPU_FIELDCNT];
307         int bar_sz;
308         char *bar;
309 S_STAT_END(cpu_stat)
310
311
312 static 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
366 static 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
381 S_STAT(int_stat)
382         ullong old;
383         int no;
384 S_STAT_END(int_stat)
385
386 static 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
402 static 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
416 S_STAT(ctx_stat)
417         ullong old;
418 S_STAT_END(ctx_stat)
419
420 static 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
436 static 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
444 S_STAT(blk_stat)
445         const char* lookfor;
446         ullong old[2];
447 S_STAT_END(blk_stat)
448
449 static 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
478 static 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
487 S_STAT(fork_stat)
488         ullong old;
489 S_STAT_END(fork_stat)
490
491 static 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
502 static 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
518 static 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
530 S_STAT(if_stat)
531         ullong old[4];
532         const char *device;
533         char *device_colon;
534 S_STAT_END(if_stat)
535
536 static 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
558 static 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
574 S_STAT(mem_stat)
575         char opt;
576 S_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
613 static 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
648 static 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
657 S_STAT(swp_stat)
658 S_STAT_END(swp_stat)
659
660 static 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
673 static 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
681 S_STAT(fd_stat)
682 S_STAT_END(fd_stat)
683
684 static 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
696 static 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
704 S_STAT(time_stat)
705         int prec;
706         int scale;
707 S_STAT_END(time_stat)
708
709 static 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
728 static 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
744 static 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
755 typedef s_stat* init_func(const char *param);
756
757 static const char options[] ALIGN1 = "ncmsfixptbdr";
758 static 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
773 int nmeter_main(int argc, char **argv);
774 int 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 }