Re: [patch] Real-Time Preemption, -RT-2.6.9-mm1-V0

From: Denis Vlasenko
Date: Tue Oct 26 2004 - 00:48:46 EST


On Monday 25 October 2004 22:40, Rui Nuno Capela wrote:
> Ingo Molnar wrote:
> >> ok, i've added it and uploaded -V0.2 together with another fix: there
> >> was a scheduler recursion possible via the delayed-put mechanism using
> >> workqueues - now it's using its own separate lists and per-CPU
> >> threads.
> >
> > -V0.2 seems to behave quite well on my testboxes - i'm unable to
> > reproduce the selinux boot hang anymore.
> >
>
> OK. RT-V0.2 boots on my laptop (P4/UP), sometimes ;)
>
> I know that my early impressions are illusive, rather subjective, but I do
> feel overall behavior is getting worst, when regarding low-latency audio
> work with jackd -R.
>
> To put things straight with RT-V0.2, I get trouble with much less load
> than even before.
>
> I noticed that something is, now and then, topping the cpu to 99%, leaving
> the system to a crawl, eventually returning back to normal. Can't figure
> out who or what, just because ps or top are stalling to silence, only
> returning results after when the crawl ends, which are of no useful
> evidence. When I'm lucky enough to let top (and gkrellm) telling me
> something, it does look like that most of the time is spent on kernel mode
> (sys time) and none of the running processes are at stake. Puzzled. It's
> just like you're about to loose confidence on the procps tools.

<shameless plug>
Maybe this program will be useful. It is designed to give you
overall system statistics without the need to scan entire /proc/NNN
forest. Together with nice -20, it will hopefully not stall.

Compiled with dietlibc. If you will have trouble compiling it, binary is
attached too.

Latest version is 0.9 but it seems I forgot it in my home box :(
</shameless plug>
--
vda


Attachment: nmeter
Description: application/executable

// Based on nanotop.c from floppyfw project
// Released under GPL
// Contact me: vda@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

//TODO:
// simplify code
// /proc/locks
// /proc/stat:
// disk_io: (3,0):(22272,17897,410702,4375,54750)
// btime 1059401962

//#include <ctype.h>
#include <sys/time.h> // gettimeofday
#include <string.h> // strstr etc
#include <stdarg.h> // f(...)
#include <fcntl.h> // O_RDONLY

#define VERSION_STR "0.7"
#define DELIM_CHAR ' '

//==============
#define NL "\n"
typedef unsigned long long ullong;
typedef unsigned long ulong;

typedef ulong sample_t;

//==============
#define proc_file_size 4096

typedef struct proc_file {
char *name;
int gen;
char *file;
} proc_file;

proc_file proc_stat = { "/proc/stat", -1 };
proc_file proc_net_dev = { "/proc/net/dev", -1 };
proc_file proc_meminfo = { "/proc/meminfo", -1 };
proc_file proc_diskstats = { "/proc/diskstats", -1 };
struct timeval tv;
int gen=-1;
int is26=0;

//==============
#if 0
#include <stdio.h>
void dprintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
#else
extern void dprintf(const char *fmt, ...) {}
#endif

//==============
char outbuf[4096];
char *cur_outbuf = outbuf;

extern inline void reset_outbuf() {
cur_outbuf = outbuf;
}

extern inline int outbuf_count() {
return cur_outbuf-outbuf;
}

extern inline void print_outbuf() {
if(cur_outbuf>outbuf) {
write(1, outbuf, cur_outbuf-outbuf);
cur_outbuf = outbuf;
}
}

void put(const char *s) {
int sz = strlen(s);
if(sz > (outbuf+sizeof(outbuf))-cur_outbuf)
sz = (outbuf+sizeof(outbuf))-cur_outbuf;
memcpy(cur_outbuf, s, sz);
cur_outbuf += sz;
}

void put_c(char c) {
if(cur_outbuf < outbuf+sizeof(outbuf))
*cur_outbuf++ = c;
}

//==============
char* simple_itoa(char *s, int sz, unsigned long v, int pad) {
//==============
s += sz;
*--s = '\0';
while (--sz > 0) {
*--s = "0123456789"[v%10];
pad--;
v /= 10;
if(!v && pad<=0) break;
}
return s;
}

//==============
int readfile_z(char *buf, int sz, const char* fname) {
//==============
int fd;
fd=open(fname,O_RDONLY);
if(fd<0) return 1;
sz = read(fd,buf,sz-1);
close(fd);
if(sz<0) {
buf[0]='\0';
return 1;
}
buf[sz]='\0';
return 0;
}

//==============
int rdval(const char* p, const char* key, sample_t *vec, ...) {
//==============
va_list arg_ptr;
int indexline;
int indexnext;

p = strstr(p,key);
if(!p) return 1;

p += strlen(key);
va_start(arg_ptr, vec);
indexline = 1;
indexnext = va_arg(arg_ptr, int);
while(1) {
while(*p==' ' || *p=='\t') p++;
if(*p=='\n' || *p=='\0') break;

if(indexline == indexnext) { // read this value
*vec++ = strtoul(p, NULL, 10);
indexnext = va_arg(arg_ptr, int);
}
while(*p > ' ') p++; // skip over value
indexline++;
}
va_end(arg_ptr);
return 0;
}

//==============
int rdval_diskstats(const char* p, sample_t *vec) {
// 1 2 3 4 5 6(rd) 7 8 9 10(wr) 11 12 13 14
// 3 0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933
// 3 1 hda1 0 0 0 0 <- ignore if only 4 fields
//==============
sample_t rd;
int indexline = 0;
vec[0] = 0;
vec[1] = 0;
while(1) {
indexline++;
while(*p==' ' || *p=='\t') p++;
if(*p=='\0') break;
if(*p=='\n') {
indexline = 0;
p++;
continue;
}
if(indexline == 6) {
rd = strtoul(p, NULL, 10);
} else
if(indexline == 10) {
vec[0] += rd; // TODO: *sectorsize (don't know how to find out sectorsize)
vec[1] += strtoul(p, NULL, 10);
while(*p!='\n' && *p!='\0') p++;
continue;
}
while(*p > ' ') p++; // skip over value
}
return 0;
}

//==============
void scale(sample_t ul) {
//==============
char buf[5];
int index = 0;
ul *= 10;
if(ul>9999*10) { // do not scale if 9999 or less
while(ul >= 10000) {
ul /= 1024;
index++;
}
}

if(!index) {/* use 1234 format */
buf[0] = " 123456789"[ul/10000];
if(buf[0]== ' ') buf[1] = " 123456789"[ul/1000%10];
else buf[1] = "0123456789"[ul/1000%10];
if(buf[1]== ' ') buf[2] = " 123456789"[ul/100%10];
else buf[2] = "0123456789"[ul/100%10];
buf[3] = "0123456789"[ul/10%10];
} else if(ul>=100) { /* use 123k format */
if( (buf[0]= " 123456789"[ul/1000]) == ' ')
buf[1] = " 123456789"[ul/100%10];
else
buf[1] = "0123456789"[ul/100%10];
buf[2] = "0123456789"[ul/10%10];
buf[3] = " kMGTEP"[index];
} else { /* use 1.2M format */
buf[0] = "0123456789"[ul/10];
buf[1] = '.';
buf[2] = "0123456789"[ul%10];
buf[3] = " kMGTEP"[index];
}
buf[4] = 0;
put(buf);
}

//==============
const char* prepare(proc_file *pf) {
if(!pf->file) pf->file = (char*)malloc(proc_file_size);
if(pf->gen != gen) {
pf->gen = gen;
readfile_z(pf->file, proc_file_size, pf->name);
}
return pf->file;
}

//==============
#define S_STAT(a) \
typedef struct a { \
struct s_stat *next; \
int (*collect)(struct a *s); \
const char *label; \
int width;

S_STAT(s_stat)
} s_stat;

#define MALLOC_STAT(type,var) type *var = (type*)malloc(sizeof(type))

//==============
S_STAT(cpu_stat)
sample_t old[4];
int bar_sz;
char *bar;
} cpu_stat;

//==============
int collect_cpu(cpu_stat *s) {
//==============
sample_t data[4];
sample_t frac[4];
sample_t all = 0;
int norm_all = 0;
int bar_sz = s->bar_sz;
char *bar = s->bar;
int i;

if(rdval(prepare(&proc_stat), "cpu ", data, 1, 2, 3, 4))
return 1;

put_c('[');

//dprintf("data1:");
for(i=0; i<4; i++) {
sample_t old = s->old[i];
if(data[i] < old) old = data[i]; //sanitize
s->old[i] = data[i];
all += (data[i] -= old);
//dprintf(" %lu",data[i]);
}
//dprintf(" all %lu\n",all);

if(all) {
//dprintf("data2:");
for(i=0; i<4; i++) {
ullong t = bar_sz*(ullong)data[i];
norm_all += data[i] = t / all;
frac[i] = t % all;
//dprintf(" %lu/%lu",data[i],frac[i]);
}
//dprintf(" norm_all %lu\n",norm_all);

while(norm_all<bar_sz) {
sample_t max=frac[0]; int pos=0;
//for(i=1; i<4; i++) if(frac[i]>=max) max=frac[i], pos=i;
if(frac[1]>=max) max=frac[1], pos=1;
if(frac[2]>=max) max=frac[2], pos=2;
if(frac[3]>=max) max=frac[3], pos=3;
frac[pos]=0; //avoid bumping same value twice
data[pos]++;
//dprintf("bumped %i\n",pos);
norm_all++;
}

//dprintf("bar_sz %i\n",bar_sz);
//dprintf("sys %i\n",data[2]);
//dprintf("usr %i\n",data[0]);
//dprintf("nice %i\n",data[1]);
memset(bar,'.',bar_sz);
memset(bar,'S',data[2]); bar+=data[2]; //sys
memset(bar,'U',data[0]); bar+=data[0]; //usr
memset(bar,'N',data[1]); bar+=data[1]; //nice
} else {
memset(bar,'?',bar_sz);
}
put(s->bar);
put_c(']');
return 0;
}

//==============
s_stat* init_cpu(const char *param) {
//==============
int sz;
MALLOC_STAT(cpu_stat,s);
s->collect = collect_cpu;
s->label = "cpu ";
s->width = 4;

sz = strtol(param, NULL, 0);
if(sz<10) sz=10;
if(sz>1000) sz=1000;

s->bar = (char*)malloc(sz+1);
s->bar[sz]=0;
s->bar_sz = sz;
s->width = sz+2;
return (s_stat*)s;
}

//==============
S_STAT(int_stat)
sample_t old;
int no;
char numlabel[6];
} int_stat;

//==============
int collect_int(int_stat *s) {
//==============
sample_t data[1];

if(rdval(prepare(&proc_stat), "intr", data, s->no))
return 1;

sample_t old = s->old;
if(data[0] < old) old = data[0]; //sanitize
s->old = data[0];
scale(data[0] - old);
return 0;
}

//==============
s_stat* init_int(const char *param) {
//==============
MALLOC_STAT(int_stat,s);
s->collect = collect_int;
s->width = 4;
if(param[0]=='\0') {
s->no = 1;
s->label = "int ";
} else {
int n = strtoul(param, NULL, 0);
s->no = n+2;
s->label = s->numlabel;
s->numlabel[0]='i';
s->numlabel[1]='n';
s->numlabel[2]='t';
s->numlabel[3]=(n<=9 ? '0'+n : n+('A'-10));
s->numlabel[4]=' ';
s->numlabel[5]='\0';
}
return (s_stat*)s;
}

//==============
S_STAT(ctx_stat)
sample_t old;
} ctx_stat;

//==============
int collect_ctx(ctx_stat *s) {
//==============
sample_t data[1];

if(rdval(prepare(&proc_stat), "ctxt", data, 1))
return 1;

sample_t old = s->old;
if(data[0] < old) old = data[0]; //sanitize
s->old = data[0];
scale(data[0] - old);
return 0;
}

//==============
s_stat* init_ctx(const char *param) {
//==============
MALLOC_STAT(ctx_stat,s);
s->collect = collect_ctx;
s->label = "ctx ";
s->width = 4;
return (s_stat*)s;
}

//==============
S_STAT(blk_stat)
const char* lookfor;
sample_t old[2];
} blk_stat;

//==============
int collect_blk24(blk_stat *s) {
//==============
sample_t data[2];
int i;
if(rdval(prepare(&proc_stat), s->lookfor, data, 1, 2))
return 1;

for(i=0; i<2; i++) {
sample_t old = s->old[i];
if(data[i] < old) old = data[i]; //sanitize
s->old[i] = data[i];
data[i] -= old;
}
scale(data[0]*1024);
put_c(' ');
scale(data[1]*1024);
return 0;
}

//==============
int collect_blk26(blk_stat *s) {
//==============
sample_t data[2];
int i;
if(rdval_diskstats(prepare(&proc_diskstats), data))
return 1;

for(i=0; i<2; i++) {
sample_t old = s->old[i];
if(data[i] < old) old = data[i]; //sanitize
s->old[i] = data[i];
data[i] -= old;
}
scale(data[0]*512);
put_c(' ');
scale(data[1]*512);
return 0;
}

//==============
int collect_blk(blk_stat *s) {
//==============
if(is26) return collect_blk26(s);
return collect_blk24(s);
}

//==============
s_stat* init_blk(const char *param) {
//==============
MALLOC_STAT(blk_stat,s);
s->collect = collect_blk;
if(param[0]=='s') {
s->label = "sio ";
s->lookfor = "swap";
} else {
s->label = "bio ";
s->lookfor = "page";
}
s->width = 9;
return (s_stat*)s;
}

//==============
S_STAT(fork_stat)
sample_t old;
} fork_stat;

//==============
int collect_fork(fork_stat *s) {
//==============
sample_t data[1];

if(rdval(prepare(&proc_stat), "processes", data, 1))
return 1;

sample_t old = s->old;
if(data[0] < old) old = data[0]; //sanitize
s->old = data[0];
scale(data[0] - old);
return 0;
}

//==============
s_stat* init_fork(const char *param) {
//==============
MALLOC_STAT(fork_stat,s);
s->collect = collect_fork;
s->label = "fork"; // no trailing space: there usually <1000 forks,
s->width = 4; // we trade usual "fork 3" for rare "fork1234"
return (s_stat*)s;
}

//==============
S_STAT(if_stat)
sample_t old[4];
const char *device;
char *device_colon;
} if_stat;

//==============
int collect_if(if_stat *s) {
//==============
sample_t data[4];
int i;

if(rdval(prepare(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11))
return 1;

//dprintf("data1:");
for(i=0; i<4; i++) {
sample_t old = s->old[i];
if(data[i] < old) old = data[i]; //sanitize
s->old[i] = data[i];
data[i] -= old;
//dprintf(" %lu",data[i]);
}
//dprintf("\n");

put_c(data[1] ? '*' : ' ');
scale(data[0]);
put_c(data[3] ? '*' : ' ');
scale(data[2]);
return 0;
}

//==============
s_stat* init_if(const char *device) {
//==============
MALLOC_STAT(if_stat,s);
s->collect = collect_if;
s->label = device;
s->width = 10;

s->device = device;
s->device_colon = (char*)malloc(strlen(device)+2);
strcpy(s->device_colon,device);
strcat(s->device_colon,":");
return (s_stat*)s;
}

//==============
S_STAT(mem_stat)
char opt;
} mem_stat;

//==============
int collect_mem(mem_stat *s) {
//==============
// total: used: free: shared: buffers: cached:
//Mem: 29306880 21946368 7360512 0 2101248 11956224
//Swap: 100655104 10207232 90447872
//MemTotal: 28620 kB
//MemFree: 7188 kB
//MemShared: 0 kB <-- ?
//Buffers: 2052 kB
//Cached: 9080 kB
//SwapCached: 2596 kB <-- ?

// Not available in 2.6:
//if(rdval(prepare(&proc_meminfo), "Mem:", data, 1, 2, 5, 6))
// return 1;
sample_t m_total;
sample_t m_free;
sample_t m_bufs;
sample_t m_cached;
if(rdval(prepare(&proc_meminfo), "MemTotal:", &m_total , 1)) return 1;
if(rdval(prepare(&proc_meminfo), "MemFree:", &m_free , 1)) return 1;
if(rdval(prepare(&proc_meminfo), "Buffers:", &m_bufs , 1)) return 1;
if(rdval(prepare(&proc_meminfo), "Cached:", &m_cached, 1)) return 1;
switch(s->opt) {
case 'f':
scale((m_free + m_bufs + m_cached)<<10); break;
case 't':
scale(m_total<<10); break;
default:
scale((m_total - m_free - m_bufs - m_cached)<<10); break;
}
return 0;
}

//==============
s_stat* init_mem(const char *param) {
//==============
MALLOC_STAT(mem_stat,s);
s->collect = collect_mem;
s->width = 4;
s->opt=param[0];
switch(param[0]) {
case 'f':
s->label = "free "; break;
case 't':
s->label = "tot "; break;
default:
s->label = "mem "; break;
}
return (s_stat*)s;
}

//==============
S_STAT(swp_stat)
} swp_stat;

//==============
int collect_swp(swp_stat *s) {
//==============
sample_t data[1];
if(rdval(prepare(&proc_meminfo), "Swap:", data, 2))
return 1;

scale(data[0]);
return 0;
}

//==============
s_stat* init_swp(const char *param) {
//==============
MALLOC_STAT(swp_stat,s);
s->collect = collect_swp;
s->label = "swp ";
s->width = 4;
return (s_stat*)s;
}

//==============
S_STAT(fd_stat)
} fd_stat;

//==============
int collect_fd(fd_stat *s) {
//==============
char file[4096];
sample_t data[2];

readfile_z(file, sizeof(file), "/proc/sys/fs/file-nr");
if(rdval(file, "", data, 1, 2))
return 1;

scale(data[0]-data[1]);
return 0;
}

//==============
s_stat* init_fd(const char *param) {
//==============
MALLOC_STAT(fd_stat,s);
s->collect = collect_fd;
s->label = "fd ";
s->width = 4;
return (s_stat*)s;
}

//==============
S_STAT(time_stat)
int prec;
int scale;
} time_stat;

//==============
int collect_time(time_stat *s) {
//==============
char buf[16]; // 12:34:56.123456<eol>
// 1234567890123456
simple_itoa(buf, 3, tv.tv_sec/(60*60)%24, 2);
buf[2] = ':';
simple_itoa(buf+3, 3, tv.tv_sec/60%60, 2);
buf[5] = ':';
simple_itoa(buf+6, 3, tv.tv_sec%60, 2);

if(s->prec) {
buf[8] = '.';
//simple_itoa(buf+9, s->prec+1, (tv.tv_usec + s->scale/2) / s->scale, s->prec);
// (fixme: round up seconds too!)
// so... rounding omitted! just use more prec if you need it! ;)
simple_itoa(buf+9, s->prec+1, tv.tv_usec / s->scale, s->prec);
}
put(buf);
return 0;
}

//==============
s_stat* init_time(const char *param) {
//==============
int prec;
MALLOC_STAT(time_stat,s);
s->collect = collect_time;
s->label = "";
prec = param[0]-'0';
if(prec<0) prec = 0;
else if(prec>6) prec = 6;
s->width = 8+prec + (prec!=0);
s->prec = prec;
s->scale = 1;
while(prec++ < 6)
s->scale *= 10;
return (s_stat*)s;
}

//==============
S_STAT(extern_stat)
int ifd,ofd;
char *buf;
char *cur;
} extern_stat;

#define WIDTH

//==============
int collect_extern(extern_stat *s) {
//==============
int sz;
char *p;
int buffered = s->cur - s->buf;

s->buf[40] = 0;
p = strchr(s->buf,'\n');
if(!p || p > s->cur) {
do {
sz = read(s->ofd,s->cur,40 - buffered);
if(sz<0) exit(1);
buffered += sz;
p = strchr(s->cur,'\n');
s->cur = s->buf+buffered;
if(p > s->cur) p=0;
} while(!p && buffered<40);
if(!p) p=s->buf+40;
}

*p++ = 0;
put(s->buf);
s->cur = s->buf;
while(p < s->buf+buffered) {
put_c('=');
*s->cur++ = *p++;
}
while(p <= s->buf+40) {
put_c('='); p++;
}
return 0;
}

//#include <stdio.h>

void myexec(void *param) {
char **argv = (char **)param;
execv(argv[0], argv);
}

int start_child(int *i,int *o,void (*f)(void*),void *param) {
int pid;
//printf("execv(%s, argv);\n",((char**)param)[0]);
{
int in[2];
int out[2];
pipe(in);
pipe(out);
pid = fork();
switch(pid) {
case -1: /* error */
return -1;
case 0: /* child */
dup2(in[0],0); close(in[0]); close(in[1]);
dup2(out[1],1); close(out[0]); close(out[1]);
f(param);
exit(1);
default: /* parent */
close(in[0]);
if(i) *i=in[1]; else close(in[1]);
if(o) *o=out[0]; else close(out[0]);
close(out[1]);
return pid;
}
}
}

//==============
s_stat* init_extern(const char *param) {
//==============
int pid;
char *argv[] = { (char*)param, 0 };
MALLOC_STAT(extern_stat,s);
s->collect = collect_extern;
s->label = "extern ";
s->width = 40;
s->buf = (char*)malloc(41);
s->cur = s->buf;
pid = start_child(&s->ifd, &s->ofd, myexec, argv);
if(pid<0) exit(1);
//printf("pid,i,o=%d %d %d\n",pid,s->ifd,s->ofd);
return (s_stat*)s;
}

//==============
char *header;
//==============
void build_n_put_hdr(s_stat *s) {
//==============
while(s) {
int l = 0;
if(s->label[0]!=' ') {
put(s->label);
l = strlen(s->label);
}
while(l <= s->width) {
put_c(' ');
l++;
}
s = s->next;
}
put_c('\n');

header = (char*)malloc(outbuf_count()+1);
memcpy(header, outbuf, outbuf_count());
header[outbuf_count()] = '\0';
//print_outbuf();
}

//==============
void put_hdr(s_stat *s) {
//==============
if(!header) build_n_put_hdr(s);
else {
put(header);
//print_outbuf();
}
}

//==============
void run_once(s_stat *s, int without_headers) {
//==============
gen++;
int first = 1;
while(s) {
if(s->label[0]!=' ') { // "[prev ][LABEL]data
if(!first) put_c(DELIM_CHAR);
if(!without_headers) put(s->label);
} else { // "prevLABELdata
put(s->label+1);
}
if(s->collect(s)) {
int w = s->width;
while(w-- > 0)
put_c('?');
}
s = s->next;
first = 0;
}
}

//==============
typedef s_stat* init_func(const char *param);

const char options[] = "ncmsfixptbe";
init_func* init_functions[] = {
init_if,
init_cpu,
init_mem,
init_swp,
init_fd,
init_int,
init_ctx,
init_fork,
init_time,
init_blk,
init_extern,
};

//==============
int main(int argc, char* argv[]) {
//==============
struct timezone tz;
s_stat *first = 0;
s_stat *last = 0;
s_stat *s;
int delta = 1000000;
int deltanz = 1000000;
int print_headers = 0;
char *final_str = "\n";
char *p;
int fd;
int i;

if(argc==1) {
put(
"nmeter " VERSION_STR " allows you to monitor your system in real time" NL
NL
"Options:" NL
"c[N] monitor CPU. N - bar size, default 10" NL
"nIFACE monitor network interface IFACE" NL
"m[f/t] monitor allocated/free/total memory" NL
"s monitor allocated swap" NL
"f monitor number of used filedescriptors" NL
"i[NN] monitor total/specific IRQ rate" NL
"x monitor context switch rate" NL
"p monitor process creation rate" NL
"b[s] monitor block io (swap io)" NL
"t[N] show time (with N decimal points)" NL
"d[N] milliseconds between updates. Default 1000" NL
"h[N] print headers above numbers (each N lines, default once)" NL
"lLABEL specify label for previous item" NL
"LLABEL same + label will be printed without surrounding blanks" NL
"r print <cr> instead of <lf> at EOL. Try it ;)" NL
);
print_outbuf();
return 0;
}

fd = open("/proc/version",O_RDONLY);
if(fd>=0) {
char buf[32];
if(0<read(fd,buf,sizeof(buf)))
is26 = (strstr(buf,"Linux version 2.6.")!=NULL);
close(fd);
}
for(i=1; i<argc; i++) {
p = strchr(options,argv[i][0]);
if(p) {
s = init_functions[p-options](argv[i]+1);
if(s) {
s->next = 0;
if(!first)
first = s;
else
last->next = s;
last = s;
}
}

// You have to see it... gcc 3.2 coded switch() as 40 element jump table
// OH NO! >>>:^O
/*
#define SW(a) switch(a) {
#define ENDSW }
#define CASE(a,b) case (b): {
#define ENDCASE }
*/
#define SW(a) do {
#define ENDSW } while(0);
#define CASE(a,b) if((a)==(b)) {
#define ENDCASE }
SW(argv[i][0])
CASE(argv[i][0],'r')
final_str = "\r";
break;
ENDCASE
CASE(argv[i][0],'d')
delta = strtol(argv[i]+1, NULL, 0)*1000;
deltanz = delta>0? delta : 1;
break;
ENDCASE
CASE(argv[i][0],'h')
if(argv[i][1]=='\0')
print_headers = -1;
else
print_headers = strtol(argv[i]+1, NULL, 0);
break;
ENDCASE
CASE(argv[i][0],'l')
if(last)
last->label=argv[i]+1;
break;
ENDCASE
CASE(argv[i][0],'L')
if(last) {
argv[i][0]=' ';
last->label=argv[i];
}
break;
ENDCASE
ENDSW
}

if(print_headers == -1) {
build_n_put_hdr(first);
print_outbuf();
}
run_once(first, print_headers);
reset_outbuf();
if(delta>=0) {
//gettimeofday(&tv,0);
gettimeofday(&tv,&tz); //
usleep(delta>1000000 ? 1000000 : delta-tv.tv_usec%deltanz);
}
while(1) {
gettimeofday(&tv,&tz);
tv.tv_sec -= tz.tz_minuteswest*60;

if(print_headers > 0 && gen%print_headers == 0)
put_hdr(first);
run_once(first, print_headers);
put(final_str);
print_outbuf();

// Negative delta -> no usleep at all
// This will hog the CPU but you can have REALLY GOOD
// time resolution ;)
// TODO: detect and avoid useless updates
// (like: nothing happens except time)
if(delta>=0) {
int rem = delta - ((ullong)tv.tv_sec*1000000+tv.tv_usec)%deltanz;
// Sometimes kernel wakes us up just a tiny bit earlier than asked
// Do not go to very short sleep in this case
if(rem < delta/128) {
rem += delta;
}
usleep(rem);
}
}

return 0;
}