Tests about a simple python3 fastcgi runner using libfcgi and the Python-C API.
python
c
wsgi
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

stats.c 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. #include "stats.h"
  2. static pyfcgi_stats_t pyfcgi_stats;
  3. static void pyfcgi_stats_add_sample(pyfcgi_stats_sample_t* samples, int sample)
  4. {
  5. samples->samples[samples->cur] = sample;
  6. samples->cur++;
  7. }
  8. int pyfcgi_stats_init()
  9. {
  10. struct sigaction act;
  11. struct itimerspec timeout;
  12. memset(&pyfcgi_stats, 0, sizeof(pyfcgi_stats_t));
  13. if(timer_create(CLOCK_REALTIME, NULL, &(pyfcgi_stats.timerid)) < 0)
  14. {
  15. pyfcgi_log(LOG_ERR,
  16. "Unable to create timer for stats collecting : %s",
  17. strerror(errno));
  18. goto err;
  19. }
  20. act.sa_handler = pyfcgi_stats_collector;
  21. sigemptyset(&act.sa_mask);
  22. act.sa_flags = 0;
  23. act.sa_restorer = NULL;
  24. if(sigaction(SIGALRM, &act, &(pyfcgi_stats.oldact)) < 0)
  25. {
  26. pyfcgi_log(LOG_ERR,
  27. "Unable to register signal handler for stats collecting : %s",
  28. strerror(errno));
  29. goto err_deltimer;
  30. }
  31. timeout.it_value.tv_sec = 1;
  32. timeout.it_value.tv_nsec = 0;
  33. timeout.it_interval = timeout.it_value;
  34. if(timer_settime(pyfcgi_stats.timerid, 0, &timeout, NULL) < 0)
  35. {
  36. pyfcgi_log(LOG_ERR,
  37. "Unable to start timer for stats collecting : %s",
  38. strerror(errno));
  39. goto err_sigrestore;
  40. }
  41. return 0;
  42. err_sigrestore:
  43. sigaction(SIGALRM, &(pyfcgi_stats.oldact), NULL);
  44. err_deltimer:
  45. timer_delete(pyfcgi_stats.timerid);
  46. err:
  47. memset(&pyfcgi_stats, 0, sizeof(pyfcgi_stats_t));
  48. return -1;
  49. }
  50. void pyfcgi_stats_collector(int signum)
  51. {
  52. int ret;
  53. pyfcgi_stats_t *stats;
  54. pyfcgi_stats_shm_t shm_data;
  55. stats = &pyfcgi_stats;
  56. stats->reqs.samples[stats->reqs.cur] = 0;
  57. while( !(ret = sem_trywait(PyFCGI_SEM(SEM_WREQS).sem)) )
  58. {
  59. stats->reqs.samples[stats->reqs.cur]++;
  60. }
  61. stats->reqs.cur++;
  62. stats->reqs.cur %= PYFCGI_STATS_SZ;
  63. if(pyfcgi_stats_get_shm(&shm_data) < 0)
  64. {
  65. kill(getpid(), SIGTERM);
  66. }
  67. pyfcgi_stats_add_sample(&(stats->wcount), shm_data.nworker);
  68. pyfcgi_stats_add_sample(&(stats->load),
  69. shm_data.nworker - shm_data.pool_load);
  70. return;
  71. }
  72. size_t pyfcgi_stats_format()
  73. {
  74. char uptime[80], since[80];
  75. char ds[18], hs[5], ms[5], ss[5];
  76. unsigned long diff, d;
  77. short s, m, h;
  78. struct tm *tm;
  79. tm = localtime(&PyFCGI_conf.context.uptime);
  80. if(!strftime(since, 80, "%F %H:%M:%S%z", tm))
  81. {
  82. pyfcgi_log(LOG_WARNING, "Date too long !");
  83. strcpy(since, "???");
  84. }
  85. diff = (unsigned long)difftime(time(NULL), PyFCGI_conf.context.uptime);
  86. s = diff%60;
  87. diff /= 60;
  88. m = diff % 60;
  89. diff /= 60;
  90. h = diff % 24;
  91. diff /=24;
  92. d = diff;
  93. if(d) { snprintf(ds, 18, "%02ldd ", d); }
  94. else { ds[0] = '\0'; }
  95. if(h || *ds) { snprintf(hs, 5, "%02dh ", h); }
  96. else { hs[0] = '\0'; }
  97. if(m || *hs) { snprintf(ms, 5, "%02dm ", m); }
  98. else { ms[0] = '\0'; }
  99. if(s || *ms) { snprintf(ss, 5, "%02ds ", s); }
  100. else { ss[0] = '\0'; }
  101. snprintf(uptime, 80, "%s%s%s%s", ds, hs, ms, ss);
  102. pyfcgi_stats.buff_ptr = 0;
  103. pyfcgi_stats_buffprintf("%s\nUptime : %s since %s\n",
  104. PACKAGE_STRING, uptime, since);
  105. // Request counter stats formating
  106. double avgs[4];
  107. int last_rs, ret;
  108. do
  109. {
  110. last_rs = pyfcgi_stats.reqs.cur;
  111. ret = pyfcgi_stats_avg(pyfcgi_stats.reqs.samples,
  112. &(pyfcgi_stats.reqs.cur), &last_rs, avgs);
  113. } while(ret < 0 && errno == EINTR);
  114. pyfcgi_stats_buffprintf("Requests stats :\n1s:%dr/s 1m:%.2fr/s 5m:%.2fr/s 10m:%.2fr/s 15m:%.2fr/s\n",
  115. last_rs, avgs[0], avgs[1], avgs[2], avgs[3]);
  116. // Worker counter stats formating
  117. do
  118. {
  119. last_rs = pyfcgi_stats.wcount.cur;
  120. pyfcgi_stats_avg(pyfcgi_stats.wcount.samples,&(pyfcgi_stats.wcount.cur),
  121. &(last_rs), avgs);
  122. } while(ret < 0 && errno == EINTR);
  123. pyfcgi_stats_buffprintf("Worker count :\n1s:%d 1m:%.2f 5m:%.2f 10m:%.2f 15m:%.2f\n",
  124. last_rs, avgs[0], avgs[1], avgs[2], avgs[3]);
  125. // Load stats formating
  126. do
  127. {
  128. last_rs = pyfcgi_stats.load.cur;
  129. pyfcgi_stats_avg(pyfcgi_stats.load.samples,&(pyfcgi_stats.load.cur),
  130. &(last_rs), avgs);
  131. } while(ret < 0 && errno == EINTR);
  132. pyfcgi_stats_buffprintf("Load average :\n1s:%d 1m:%.2f 5m:%.2f 10m:%.2f 15m:%.2f\n",
  133. last_rs, avgs[0], avgs[1], avgs[2], avgs[3]);
  134. return pyfcgi_stats.buff_ptr;
  135. }
  136. int pyfcgi_stats_avg(const int data[PYFCGI_STATS_SZ], int *idx_nxt, int *last,
  137. double avgs[4])
  138. {
  139. double r15, r10, r5, r1, rtmp;
  140. unsigned long stmp; // stores a 60s req sum
  141. int i, cur, idx, first;
  142. r15 = r10 = r5 = r1 = stmp = 0;
  143. first = *idx_nxt;
  144. //Block interrupt/ALARM ??
  145. for(i=0; i<PYFCGI_STATS_SZ; i++)
  146. {
  147. if(first != *idx_nxt)
  148. {
  149. errno = EINTR;
  150. return -1;
  151. }
  152. idx = (*idx_nxt- i - 1);
  153. idx = (idx<0)?PYFCGI_STATS_SZ + idx:idx;
  154. idx %= PYFCGI_STATS_SZ;
  155. cur = data[idx];
  156. if(!i || i%60)
  157. {
  158. stmp += cur;
  159. continue;
  160. }
  161. rtmp = (long double)stmp / 60;
  162. if(i==60) { r1 = rtmp; }
  163. if(i<=5*60) { r5 += rtmp; }
  164. if(i<=10*60) { r10 += rtmp; }
  165. if(i<=15*60) { r15 += rtmp; }
  166. stmp = cur;
  167. }
  168. r15 += (long double)stmp / 60;
  169. //Restore interrupt/ALARM ??
  170. r5 /= 5;
  171. r10 /= 10;
  172. r15 /= 15;
  173. *last = data[*idx_nxt-1];
  174. avgs[0] = r1;
  175. avgs[1] = r5;
  176. avgs[2] = r10;
  177. avgs[3] = r15;
  178. return 0;
  179. }
  180. int pyfcgi_stats_avg_const(const int data[PYFCGI_STATS_SZ], int *idx_nxt, int *last,
  181. double avgs[4])
  182. {
  183. double r15, r10, r5, r1, rtmp, dmax;
  184. unsigned long stmp; // stores a 60s req sum
  185. int i, cur, idx, first;
  186. int uptime, max;
  187. uptime = time(NULL) - PyFCGI_conf.context.uptime;
  188. max = (uptime > PYFCGI_STATS_SZ) ? PYFCGI_STATS_SZ:uptime;
  189. r15 = r10 = r5 = r1 = stmp = rtmp = 0;
  190. first = *idx_nxt;
  191. //Block interrupt/ALARM ??
  192. i = 0;
  193. do
  194. {
  195. if(first != *idx_nxt)
  196. {
  197. errno = EINTR;
  198. return -1;
  199. }
  200. idx = (*idx_nxt- i - 1);
  201. idx = (idx<0)?PYFCGI_STATS_SZ + idx:idx;
  202. idx %= PYFCGI_STATS_SZ;
  203. cur = data[idx];
  204. if((!i || i%60) && i < max-1)
  205. {
  206. stmp += cur;
  207. i++;
  208. continue;
  209. }
  210. rtmp = (long double)stmp / ((i%60)?i%60:60);
  211. if(i<=60) { r1 = rtmp; }
  212. else if(i%60) { rtmp = (double)rtmp / (60.0 / (i%60)); }
  213. if(i<=5*60) { r5 += rtmp; }
  214. if(i<=10*60) { r10 += rtmp; }
  215. if(i<=15*60) { r15 += rtmp; }
  216. stmp = cur;
  217. i++;
  218. }while(i<max);
  219. //Restore interrupt/ALARM ??
  220. *last = data[*idx_nxt-1];
  221. dmax = (max<=60)?1:(double)max/60;
  222. r5 /= (max >= 300)?5:dmax;
  223. r10 /= (max >= 600)?10:dmax;
  224. r15 /= (max >= 900)?15:dmax;
  225. avgs[0] = r1;
  226. avgs[1] = r5;
  227. avgs[2] = r10;
  228. avgs[3] = r15;
  229. return 0;
  230. }
  231. void pyfcgi_stats_buffprintf(const char *fmt, ...)
  232. {
  233. va_list ap;
  234. size_t fsz, left;
  235. while(1)
  236. {
  237. left = pyfcgi_stats.buff_len - pyfcgi_stats.buff_ptr;
  238. va_start(ap, fmt);
  239. fsz = vsnprintf(pyfcgi_stats.buff + pyfcgi_stats.buff_ptr,
  240. left, fmt, ap);
  241. va_end(ap);
  242. if(fsz >= left)
  243. {
  244. if(pyfcgi_stats_reqbuff(pyfcgi_stats.buff_ptr + fsz) < 0)
  245. {
  246. exit(PYFCGI_ERR);
  247. }
  248. continue;
  249. }
  250. pyfcgi_stats.buff_ptr += fsz;
  251. break;
  252. }
  253. }
  254. int pyfcgi_stats_reqbuff(size_t sz)
  255. {
  256. void *tmp;
  257. if(sz <= pyfcgi_stats.buff_len) { return 0; }
  258. sz = ((sz>>11)+1)<<11;
  259. if( !(tmp = realloc(pyfcgi_stats.buff, sz)) )
  260. {
  261. pyfcgi_log(LOG_ALERT, "Unable to reallocate stats buffer : %s",
  262. strerror(errno));
  263. return -1;
  264. }
  265. pyfcgi_stats.buff_len = sz;
  266. pyfcgi_stats.buff = tmp;
  267. return 0;
  268. }
  269. const char *pyfcgi_stats_buff(const char **buff, size_t* len)
  270. {
  271. *buff = pyfcgi_stats.buff;
  272. *len = pyfcgi_stats.buff_ptr;
  273. return *buff;
  274. }
  275. int pyfcgi_stats_get_shm(pyfcgi_stats_shm_t *res)
  276. {
  277. short retry;
  278. int err;
  279. struct timespec req;
  280. req.tv_sec = 0;
  281. req.tv_nsec = 10000000; //0.01s
  282. retry = 0;
  283. while(1)
  284. {
  285. if(sem_trywait(PyFCGI_SEM(SEM_STATS).sem) < 0)
  286. {
  287. err = errno;
  288. if(err == EAGAIN)
  289. {
  290. if(retry >= 5)
  291. {
  292. pyfcgi_log(LOG_ALERT,
  293. "Deadlock on SEM_STATS");
  294. return -1;
  295. }
  296. nanosleep(&req, NULL);
  297. continue;
  298. }
  299. pyfcgi_log(LOG_ALERT,
  300. "Unable to wait stats semaphore : %s",
  301. strerror(err));
  302. return -1;
  303. }
  304. break;
  305. }
  306. *res = *(pyfcgi_stats_shm_t*)PyFCGI_conf.shm.ptr;
  307. if(sem_post(PyFCGI_SEM(SEM_STATS).sem) < 0)
  308. {
  309. pyfcgi_log(LOG_ALERT, "Unable to post sem at shm update : %s",
  310. strerror(errno));
  311. return -1;
  312. }
  313. return 0;
  314. }