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