source: MondoRescue/branches/3.3/mindi-busybox/procps/nmeter.c@ 3723

Last change on this file since 3723 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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