Search squid archive

Re: FATAL: xcalloc

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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:vda@xxxxxxxxxxxxx] 
> Sent: 18 November 2005 11:42
> To: squid-users@xxxxxxxxxxxxxxx
> Cc: Tomas Palfi
> Subject: Re:  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: vda@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

//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;
}

[Index of Archives]     [Linux Audio Users]     [Samba]     [Big List of Linux Books]     [Linux USB]     [Yosemite News]

  Powered by Linux