#include "stats.h" static pyfcgi_stats_t pyfcgi_stats; static void pyfcgi_stats_add_sample(pyfcgi_stats_sample_t* samples, int sample) { samples->samples[samples->cur] = sample; samples->cur++; samples->cur %= PYFCGI_STATS_SZ; } int pyfcgi_stats_init() { struct sigaction act; struct itimerspec timeout; struct sigevent sev; memset(&pyfcgi_stats, 0, sizeof(pyfcgi_stats_t)); sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = PYFCGI_STATS_SIGALRM; sev.sigev_value.sival_int = 0; if(timer_create(CLOCK_REALTIME, &sev, &(pyfcgi_stats.timerid)) < 0) { pyfcgi_log(LOG_ERR, "Unable to create timer for stats collecting : %s", strerror(errno)); goto err; } act.sa_handler = pyfcgi_stats_collector; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_restorer = NULL; if(sigaction(PYFCGI_STATS_SIGALRM, &act, &(pyfcgi_stats.oldact)) < 0) { pyfcgi_log(LOG_ERR, "Unable to register signal handler for stats collecting : %s", strerror(errno)); goto err_deltimer; } timeout.it_value.tv_sec = 1; timeout.it_value.tv_nsec = 0; timeout.it_interval = timeout.it_value; if(timer_settime(pyfcgi_stats.timerid, 0, &timeout, NULL) < 0) { pyfcgi_log(LOG_ERR, "Unable to start timer for stats collecting : %s", strerror(errno)); goto err_sigrestore; } return 0; err_sigrestore: sigaction(SIGALRM, &(pyfcgi_stats.oldact), NULL); err_deltimer: timer_delete(pyfcgi_stats.timerid); err: memset(&pyfcgi_stats, 0, sizeof(pyfcgi_stats_t)); return -1; } void pyfcgi_stats_collector(int signum) { int ret; pyfcgi_stats_t *stats; pyfcgi_stats_shm_t shm_data; stats = &pyfcgi_stats; stats->reqs.samples[stats->reqs.cur] = 0; while( !(ret = sem_trywait(PyFCGI_SEM(SEM_WREQS).sem)) ) { if(ret < 0) { pyfcgi_IPC_close(IPC_WREQS); if(pyfcgi_IPC_init(IPC_WREQS)) { pyfcgi_log(LOG_ALERT, "Unable to gain access again to IPC_WREQS, exiting..."); kill(PyFCGI_conf.context.pid, SIGTERM); } continue; } stats->reqs.samples[stats->reqs.cur]++; } stats->reqs.cur++; stats->reqs.cur %= PYFCGI_STATS_SZ; if(pyfcgi_stats_get_shm(&shm_data) < 0) { kill(getpid(), SIGTERM); } pyfcgi_stats_add_sample(&(stats->wcount), shm_data.nworker); pyfcgi_stats_add_sample(&(stats->load), shm_data.nworker - shm_data.pool_load); return; } size_t pyfcgi_stats_format() { char uptime[80], since[80]; char ds[18], hs[5], ms[5], ss[5]; unsigned long diff, d; short s, m, h; struct tm *tm; tm = localtime(&PyFCGI_conf.context.uptime); if(!strftime(since, 80, "%F %H:%M:%S%z", tm)) { pyfcgi_log(LOG_WARNING, "Date too long !"); strcpy(since, "???"); } diff = (unsigned long)difftime(time(NULL), PyFCGI_conf.context.uptime); s = diff%60; diff /= 60; m = diff % 60; diff /= 60; h = diff % 24; diff /=24; d = diff; if(d) { snprintf(ds, 18, "%02ldd ", d); } else { ds[0] = '\0'; } if(h || *ds) { snprintf(hs, 5, "%02dh ", h); } else { hs[0] = '\0'; } if(m || *hs) { snprintf(ms, 5, "%02dm ", m); } else { ms[0] = '\0'; } if(s || *ms) { snprintf(ss, 5, "%02ds ", s); } else { ss[0] = '\0'; } snprintf(uptime, 80, "%s%s%s%s", ds, hs, ms, ss); pyfcgi_stats.buff_ptr = 0; pyfcgi_stats_buffprintf("%s\nUptime : %s since %s\n", PACKAGE_STRING, uptime, since); // Request counter stats formating double avgs[4]; int last_rs, ret; do { last_rs = pyfcgi_stats.reqs.cur; ret = pyfcgi_stats_avg(pyfcgi_stats.reqs.samples, &(pyfcgi_stats.reqs.cur), &last_rs, avgs); } while(ret < 0 && errno == EINTR); pyfcgi_stats_buffprintf("Requests stats :\n1s:%dr/s 1m:%.2fr/s 5m:%.2fr/s 10m:%.2fr/s 15m:%.2fr/s\n", last_rs, avgs[0], avgs[1], avgs[2], avgs[3]); // Worker counter stats formating do { last_rs = pyfcgi_stats.wcount.cur; pyfcgi_stats_avg(pyfcgi_stats.wcount.samples,&(pyfcgi_stats.wcount.cur), &(last_rs), avgs); } while(ret < 0 && errno == EINTR); pyfcgi_stats_buffprintf("Worker count :\n1s:%d 1m:%.2f 5m:%.2f 10m:%.2f 15m:%.2f\n", last_rs, avgs[0], avgs[1], avgs[2], avgs[3]); // Load stats formating do { last_rs = pyfcgi_stats.load.cur; pyfcgi_stats_avg(pyfcgi_stats.load.samples,&(pyfcgi_stats.load.cur), &(last_rs), avgs); } while(ret < 0 && errno == EINTR); pyfcgi_stats_buffprintf("Load average :\n1s:%d 1m:%.2f 5m:%.2f 10m:%.2f 15m:%.2f\n", last_rs, avgs[0], avgs[1], avgs[2], avgs[3]); return pyfcgi_stats.buff_ptr; } int pyfcgi_stats_avg(const int data[PYFCGI_STATS_SZ], int *idx_nxt, int *last, double avgs[4]) { double r15, r10, r5, r1, rtmp; unsigned long stmp; // stores a 60s req sum int i, cur, idx, first; r15 = r10 = r5 = r1 = stmp = 0; first = *idx_nxt; //Block interrupt/ALARM ?? for(i=0; i PYFCGI_STATS_SZ) ? PYFCGI_STATS_SZ:uptime; r15 = r10 = r5 = r1 = stmp = rtmp = 0; first = *idx_nxt; //Block interrupt/ALARM ?? i = 0; do { if(first != *idx_nxt) { errno = EINTR; return -1; } idx = (*idx_nxt- i - 1); idx = (idx<0)?PYFCGI_STATS_SZ + idx:idx; idx %= PYFCGI_STATS_SZ; cur = data[idx]; if((!i || i%60) && i < max-1) { stmp += cur; i++; continue; } rtmp = (long double)stmp / ((i%60)?i%60:60); if(i<=60) { r1 = rtmp; } else if(i%60) { rtmp = (double)rtmp / (60.0 / (i%60)); } if(i<=5*60) { r5 += rtmp; } if(i<=10*60) { r10 += rtmp; } if(i<=15*60) { r15 += rtmp; } stmp = cur; i++; }while(i= 300)?5:dmax; r10 /= (max >= 600)?10:dmax; r15 /= (max >= 900)?15:dmax; avgs[0] = r1; avgs[1] = r5; avgs[2] = r10; avgs[3] = r15; return 0; } void pyfcgi_stats_buffprintf(const char *fmt, ...) { va_list ap; size_t fsz, left; while(1) { left = pyfcgi_stats.buff_len - pyfcgi_stats.buff_ptr; va_start(ap, fmt); fsz = vsnprintf(pyfcgi_stats.buff + pyfcgi_stats.buff_ptr, left, fmt, ap); va_end(ap); if(fsz >= left) { if(pyfcgi_stats_reqbuff(pyfcgi_stats.buff_ptr + fsz) < 0) { exit(PYFCGI_ERR); } continue; } pyfcgi_stats.buff_ptr += fsz; break; } } int pyfcgi_stats_reqbuff(size_t sz) { void *tmp; if(sz <= pyfcgi_stats.buff_len) { return 0; } sz = ((sz>>11)+1)<<11; if( !(tmp = realloc(pyfcgi_stats.buff, sz)) ) { pyfcgi_log(LOG_ALERT, "Unable to reallocate stats buffer : %s", strerror(errno)); return -1; } pyfcgi_stats.buff_len = sz; pyfcgi_stats.buff = tmp; return 0; } const char *pyfcgi_stats_buff(const char **buff, size_t* len) { *buff = pyfcgi_stats.buff; *len = pyfcgi_stats.buff_ptr; return *buff; } int pyfcgi_stats_get_shm(pyfcgi_stats_shm_t *res) { short retry; int err; struct timespec req; req.tv_sec = 0; req.tv_nsec = 10000000; //0.01s retry = 0; while(1) { if(sem_trywait(PyFCGI_SEM(SEM_STATS).sem) < 0) { err = errno; if(err == EAGAIN) { if(retry >= 5) { pyfcgi_log(LOG_ALERT, "Deadlock on SEM_STATS"); return -1; } nanosleep(&req, NULL); continue; } pyfcgi_log(LOG_ALERT, "Unable to wait stats semaphore : %s", strerror(err)); return -1; } break; } *res = *(pyfcgi_stats_shm_t*)PyFCGI_conf.shm.ptr; err = 0; if(res->pool_load == -1) { pyfcgi_log(LOG_INFO, "Dropping data : pool_handler seems to encounter problems"); err = 1; memset(res, 0, sizeof(pyfcgi_stats_shm_t)); } if(sem_post(PyFCGI_SEM(SEM_STATS).sem) < 0) { pyfcgi_log(LOG_ALERT, "Unable to post sem at shm update : %s", strerror(errno)); return -1; } return err?-1:0; }