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