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