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