~vcs-imports/busybox/trunk

« back to all changes in this revision

Viewing changes to procps/nmeter.c

  • Committer: Eric Andersen
  • Date: 1999-11-24 09:04:33 UTC
  • Revision ID: git-v1:b99df0fd65abe3245fa2d04115326100847f865e
First draft

Show diffs side-by-side

added added

removed removed

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