source: MondoRescue/branches/stable/mindi-busybox/procps/nmeter.c

Last change on this file was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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