On Friday 18 November 2005 14:23, Tomas Palfi wrote:
> Denis,
> 
> That's a fair comment but it doesn't look as if it's running out of
> memory.  It's like as if the OS did not want to release more memory for
> squid or something.  But just for information that's top output.
> 
> last pid: 21404; load averages: 0.00,  0.01,  0.00  up 36+23:31:29 12:11:02
> 55 processes:  1 running, 54 sleeping
> CPU states:  0.0% user,  0.0% nice,  1.2% system,  7.8% interrupt, 91.1% idle
> Mem: 515M Active, 1106M Inact, 189M Wired, 68M Cache, 112M Buf, 125M Free
> Swap: 5120M Total, 5120M Free
> 
>   PID USERNAME PRI NICE   SIZE    RES STATE    TIME   WCPU    CPU COMMAND
> 18208 squid     96    0   344M   343M select   1:59  0.00%  0.00% squid

This is the normally-running squid I guess. Suppose there's a bug
and squid tries to alloc a million of 4k blocks. It will swell enormously
and then die, you won't see it in top.

1. Upgrade to latest squid.
2. Run a daemon which logs system memory consumption every second,
   save output to file. Watch for memory usage spikes. Do they coincide
   with squid deaths?
3. Increase squid logging.
4. Instrument xcalloc/free routines to report totals, etc...

(memory logger source is attached)

>   405 root      96    0  2964K  1424K select   0:41  0.00%  0.00% ntpd
>   433 root      96    0  3472K  2256K select   0:28  0.00%  0.00% sendmail
>   450 root       8    0  1364K   928K nanslp   0:07  0.00%  0.00% cron
>   298 root      96    0  1324K   804K select   0:03  0.00%  0.00% syslogd
>   378 root      96    0  1244K   684K select   0:02  0.00%  0.00% usbd
>   437 root      20    0  2964K  1428K pause    0:02  0.00%  0.00% ntpd
>   438 smmsp     20    0  3356K  2032K pause    0:01  0.00%  0.00% sendmail
> 18229 squid     -8    0  1188K   676K piperd   0:00  0.00%  0.00% unlinkd
> 21358 squid      4    0  2852K  1616K sbwait   0:00  0.00%  0.00% 
> suid_ldap_group
> 21391 root      96    0  2392K  1576K RUN      0:00  0.00%  0.00% top
> 21357 squid      4    0  2752K  1404K sbwait   0:00  0.00%  0.00% 
> squid_ldap_auth
> 21367 squid      4    0  2748K  1400K sbwait   0:00  0.00%  0.00% 
> squid_ldap_group
> 21362 squid      4    0  2748K  1400K sbwait   0:00  0.00%  0.00% 
> squid_ldap_group
> 21350 squid      4    0  2752K  1404K sbwait   0:00  0.00%  0.00% 
> squid_ldap_auth
> 21348 squid      4    0  2856K  1600K sbwait   0:00  0.00%  0.00% 
> squid_ldap_auth
> 21364 squid      4    0  2748K  1400K sbwait   0:00  0.00%  0.00% 
> squid_ldap_group
> 21356 squid      4    0  2752K  1404K sbwait   0:00  0.00%  0.00% 
> squid_ldap_auth
> 
> Tomas
> 
> --
> tp
> 
> 
> 
> 
> -----Original Message-----
> From: Denis Vlasenko [mailto:[EMAIL PROTECTED] 
> Sent: 18 November 2005 11:42
> To: [email protected]
> Cc: Tomas Palfi
> Subject: Re: [squid-users] FATAL: xcalloc
> 
> On Friday 18 November 2005 12:54, Tomas Palfi wrote:
> > To all,
> > 
> > Every so often, almost daily, I get this message in logs and squid
> > reloads.
> > 
> > FATAL: xcalloc: Unable to allocate 1 blocks of 4108 bytes!
> > 
> > Squid Cache (Version 2.5.STABLE10): Terminated abnormally.
> > CPU Usage: 167.029 seconds = 122.739 user + 44.290 sys
> > Maximum Resident Size: 526264 KB
> > Page faults with physical i/o: 0
> > 
> > I have checked almost all references to this problem and some of them
> > are pointing to not enough memory on the server, which is not my case
> as
> > I have plenty of that.  What puzzles me is the amount of memory being
> > allocated to a squid process by FreeBSD.  I am running several other
> > caches without any such problems, however, this one is the biggest
> > cache.
> 
> You did not actually show any numbers on amount of used memory. top
> etc...
> --
> vda
> 
> _______________________________________________________________________
> 
> This e-mail has been scanned by Messagelabs
> _______________________________________________________________________
> 
> PRIVACY & CONFIDENTIALITY
> 
> This e-mail is private and confidential.  If you have, or suspect you have 
> received this message in error please notify the sender as soon as possible 
> and remove from your system.  You may not copy, distribute or take any action 
> in reliance on it. Thank you for your co-operation.
> 
> Please note that whilst best efforts are made, neither the company nor the 
> sender accepts any responsibility for viruses and it is your responsibility 
> to scan the email and attachments (if any).
> 
> This e-mail has been automatically scanned for viruses by MessageLabs.
> 
> 
// Based on nanotop.c from floppyfw project
// Released under GPL
// Contact me: [EMAIL PROTECTED]

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

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

#define VERSION_STR "0.95"
#define DELIM_CHAR ' '

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

//typedef ulong sample_t;
//#define STR2SAMPLE strtoul
// Needed if you have 512+ RAM, or else at least mem display wouldn't be ok:
typedef ullong sample_t;
#define STR2SAMPLE strtoull

// localtime is slower, bigger (+1 kbyte), but handles
// tz transition correctly
#define USE_LOCALTIME	

//==============
#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_loadavg = { "/proc/loadavg", -1 };
proc_file proc_net_dev = { "/proc/net/dev", -1 };
proc_file proc_meminfo = { "/proc/meminfo", -1 };
proc_file proc_diskstats = { "/proc/diskstats", -1 };
// Sample #
int gen = -1;
// Linux 2.6? (othervise assumes 2.4)
int is26 = 0;
struct timeval tv;
#ifndef USE_LOCALTIME
struct timezone tz;
#endif

//==============
#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 inline void dprintf(const char *fmt, ...) {}
#endif

//==============
int delta = 1000000;
int deltanz = 1000000;
int need_seconds = 0;

//==============
// This allows "show only if changed" functionality
#if 0
int outbuf_idx = 0;
char outbuf[2][4096];
#else
enum { outbuf_idx = 0 };
char outbuf[1][4096];
#endif
char *cur_outbuf = outbuf[0];

static inline void reset_outbuf() {
    cur_outbuf = outbuf[outbuf_idx];
}

//==============
static inline int outbuf_count() {
    return cur_outbuf-outbuf[outbuf_idx];
}

static void print_outbuf() {
// This allows "show only if changed" functionality
#if 0
    int sz = cur_outbuf-outbuf[outbuf_idx];
    if(sz>0 && memcmp(outbuf[outbuf_idx], outbuf[outbuf_idx^1], sz)) {
	write(1, outbuf[outbuf_idx], sz);
	outbuf_idx ^= 1;
    }
    cur_outbuf = outbuf[outbuf_idx];
#else
    int sz = cur_outbuf-outbuf[outbuf_idx];
    if(sz>0) {
	write(1, outbuf[outbuf_idx], sz);
	cur_outbuf = outbuf[outbuf_idx];
    }
#endif
}

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

void put_c(char c) {
    if(cur_outbuf < outbuf[outbuf_idx]+sizeof(outbuf[outbuf_idx]))
	*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;
    // We are not checking for short reads (valid only because we are
    // reading /proc files)
    sz = read(fd, buf, sz-1);
    close(fd);
    if(sz<0) {
	buf[0] = '\0';
	return 1;
    }
    buf[sz] = '\0';
    return 0;
}

//==============
const char* prepare(proc_file *pf) {
//==============
    if(pf->gen != gen) {
	pf->gen = gen;
	// We allocate proc_file_size bytes. This wastes memory,
	// but allows us to allocate only once (at first sample)
	// per proc file, and reuse buffer for each sample
	if(!pf->file) pf->file = (char*)malloc(proc_file_size);
	readfile_z(pf->file, proc_file_size, pf->name);
    }
    return pf->file;
}

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

    p = strstr(p, key);
    if(!p) return 1;
	
    p += strlen(key);
    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++ = conv(p);
            indexnext = va_arg(arg_ptr, int);
        }
    	while(*p > ' ') p++; // skip over value
        indexline++;
    }
    return 0;
}

//==============
sample_t read_decimal_sample(const char *p) {
//==============
    return STR2SAMPLE(p, NULL, 10);
}

//==============
int rdval(const char* p, const char* key, sample_t *vec, ...) {
// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0":
// rdval(file_contents, "string_to_find", result_vector, value#, value#...)
// value# are 1-based
//==============
    va_list arg_ptr;
    int result;

    va_start(arg_ptr, vec);
    result = vrdval(p, key, read_decimal_sample, vec, arg_ptr);
    va_end(arg_ptr);

    return result;
}

//==============
sample_t read_after_slash(const char *p) {
//==============
    p = strchr(p, '/');
    if(!p) return 0;
    return STR2SAMPLE(p+1, NULL, 10);
}

//==============
int rdval_loadavg(const char* p, sample_t *vec, ...) {
// Parses files with lines like "... ... ... 3/148 ...."
//==============
    va_list arg_ptr;
    int result;

    va_start(arg_ptr, vec);
    result = vrdval(p, "", read_after_slash, vec, arg_ptr);
    va_end(arg_ptr);

    return result;
}

//==============
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 = STR2SAMPLE(p, NULL, 10);
        } else
        if(indexline == 10) {
            vec[0] += rd;  // TODO: *sectorsize (don't know how to find out sectorsize)
            vec[1] += STR2SAMPLE(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) {	// >= 9999: 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>=10*10) {	// scaled value is >=10: use 123M format
	buf[0] = " 123456789"[ul/1000];
	if(buf[0]==' ') buf[1] = " 123456789"[ul/100%10];
                   else buf[1] = "0123456789"[ul/100%10];
	buf[2] = "0123456789"[ul/10%10];
	buf[3] = " kMGTEP"[index];
    } else {			// scaled value is <10: 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);
}

//==============
#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))

//==============
//     user nice system idle  iowait irq  softirq (last 3 only in 2.6)
//cpu  649369 0 341297 4336769 11640 7122 1183
//cpuN 649369 0 341297 4336769 11640 7122 1183
#define N 7
S_STAT(cpu_stat)
    sample_t old[N];
    int bar_sz;
    char *bar;
} cpu_stat;

//==============
int collect_cpu(cpu_stat *s) {
//==============
    sample_t data[N] = { 0, 0, 0, 0, 0, 0, 0 };
    sample_t frac[N] = { 0, 0, 0, 0, 0, 0, 0 };
    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, 5, 6, 7))
	return 1;
    
    put_c('[');

    for(i=0; i<N; i++) {
	sample_t old = s->old[i];
	if(data[i] < old) old = data[i];	//sanitize
        s->old[i] = data[i];
        all += (data[i] -= old);
    }

    if(all) {
	for(i=0; i<N; i++) {
	    ullong t = bar_sz*(ullong)data[i];
	    norm_all += data[i] = t / all;
	    frac[i] = t % all;
	}
    
	while(norm_all<bar_sz) {
	    sample_t max = frac[0];
	    int pos = 0;
	    for(i=1; i<N; i++) {
		if(frac[i]>=max) max = frac[i], pos = i;
	    }
	    frac[pos] = 0;	//avoid bumping same value twice
	    data[pos]++;
	    norm_all++;
        }
    
	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
	memset(bar, 'D', data[4]); bar += data[4]; //iowait
	memset(bar, 'I', data[5]); bar += data[5]; //irq
	memset(bar, 'i', data[6]); bar += data[6]; //softirq
    } 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_thread_nr(fork_stat *s) {
//==============
    sample_t data[1];

    if(rdval_loadavg(prepare(&proc_loadavg), data, 4))
	return 1;
    scale(data[0]);
    return 0;
}

//==============
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);
    if(*param=='n') {
	s->collect = collect_thread_nr;
	s->label = "proc ";
	s->width = 5;
    } else {
	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;

    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;
    }
    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 s_total[1];
    sample_t s_free[1];
    if(rdval(prepare(&proc_meminfo), "SwapTotal:", s_total, 1)) return 1;
    if(rdval(prepare(&proc_meminfo), "SwapFree:",  s_free , 1)) return 1;
    scale((s_total[0]-s_free[0])<<10);
    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
    int us = tv.tv_usec + s->scale/2;

#ifdef USE_LOCALTIME
/* uses localtime(). slower */
    time_t t = tv.tv_sec + (us>=1000000);
    struct tm* tm = localtime(&t);

    simple_itoa(buf, 3, tm->tm_hour, 2);
    buf[2] = ':';
    simple_itoa(buf+3, 3, tm->tm_min, 2);
    buf[5] = ':';
    simple_itoa(buf+6, 3, tm->tm_sec, 2);
#else
/* uses corrected tv from gettimeofday(). does not handle on-the-fly timezone change */
    int sec = tv.tv_sec + (us>=1000000);

    simple_itoa(buf, 3, sec/(60*60)%24, 2);
    buf[2] = ':';
    simple_itoa(buf+3, 3, sec/60%60, 2);
    buf[5] = ':';
    simple_itoa(buf+6, 3, sec%60, 2);
#endif

    if(s->prec) {
	buf[8] = '.';
	simple_itoa(buf+9, s->prec+1, us / 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;
}

//==============
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[] = "ncmsfixptb";
init_func* init_functions[] = {
    init_if,
    init_cpu,
    init_mem,
    init_swp,
    init_fd,
    init_int,
    init_ctx,
    init_fork,
    init_time,
    init_blk,
};

//#include <signal.h>
//static void setup_signals() {
//    sigset_t ss;
//
//    sigemptyset(&ss);
//    sigaddset(&ss, SIGPIPE);	
//    sigprocmask(SIG_BLOCK, &ss, NULL);
//}

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

    //setup_signals();

    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
	"	(legend: S:system U:user N:niced D:iowait I:irq i:softirq)" 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[f/n]	monitor forks/# of processes" NL
	"b	monitor block io" 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" 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.4.")==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;
	    need_seconds = (1000000%delta) != 0;
	    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);
	usleep(delta>1000000 ? 1000000 : delta-tv.tv_usec%deltanz);
    }
    while(1) {
//printf("tv.tv_sec: %d\n", (int)tv.tv_sec);
//printf("timezone: %d\n", (int)timezone);
	//tv.tv_sec -=timezone;
#ifndef USE_LOCALTIME
	gettimeofday(&tv, &tz);
	tv.tv_sec -= tz.tz_minuteswest*60; //was: -=timezone;
#else
	gettimeofday(&tv, 0);
#endif

	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;
	    /* can be commented out, will sacrifice sleep time precision a bit */
	    gettimeofday(&tv, 0);
	    if(need_seconds)
		rem = delta - ((ullong)tv.tv_sec*1000000+tv.tv_usec)%deltanz;
	    else
		rem = delta - 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;
	    }
//printf("tv.tv_usec:%d rem:%d\n", (int)tv.tv_usec, rem);
	    usleep(rem);
	}
    }

    return 0;
}

Reply via email to