blob: e83de38ec68404aa0f16c85263477fe4472eea6f [file] [log] [blame]
Denis Vlasenko1db39b22006-10-11 20:59:02 +00001/*
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}