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