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