[MEDIUM] measure and report session rate on frontend, backends and servers

With this change, all frontends, backends, and servers maintain a session
counter and a timer to compute a session rate over the last second. This
value will be very useful because it varies instantly and can be used to
check thresholds. This value is also reported in the stats in a new "rate"
column.
diff --git a/src/backend.c b/src/backend.c
index e1911c5..ed291da 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -36,6 +36,7 @@
 #include <proto/proto_http.h>
 #include <proto/proto_tcp.h>
 #include <proto/queue.h>
+#include <proto/server.h>
 #include <proto/session.h>
 #include <proto/stream_sock.h>
 #include <proto/task.h>
@@ -1912,7 +1913,7 @@
 		}
 
 		if (t->srv)
-			t->srv->cum_sess++;
+			srv_inc_sess_ctr(t->srv);
 		if (t->srv)
 			t->srv->failed_conns++;
 		t->be->failed_conns++;
diff --git a/src/client.c b/src/client.c
index 4cc3d7e..3e1ff58 100644
--- a/src/client.c
+++ b/src/client.c
@@ -33,6 +33,7 @@
 #include <proto/log.h>
 #include <proto/hdr_idx.h>
 #include <proto/proto_http.h>
+#include <proto/proxy.h>
 #include <proto/session.h>
 #include <proto/stream_interface.h>
 #include <proto/stream_sock.h>
@@ -236,7 +237,7 @@
 		s->data_source = DATA_SRC_NONE;
 
 		s->uniq_id = totalconn;
-		p->cum_feconn++;	/* cum_beconn will be increased once assigned */
+		proxy_inc_fe_ctr(p);	/* note: cum_beconn will be increased once assigned */
 
 		txn = &s->txn;
 		txn->flags = 0;
@@ -444,7 +445,7 @@
 			p->feconn_max = p->feconn;
 
 		if (s->flags & SN_BE_ASSIGNED) {
-			s->be->cum_beconn++;
+			proxy_inc_be_ctr(s->be);
 			s->be->beconn++;
 			if (s->be->beconn > s->be->beconn_max)
 				s->be->beconn_max = s->be->beconn;
diff --git a/src/dumpstats.c b/src/dumpstats.c
index dd047b7..97443a1 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -41,6 +41,7 @@
 #include <proto/buffers.h>
 #include <proto/dumpstats.h>
 #include <proto/fd.h>
+#include <proto/freq_ctr.h>
 #include <proto/pipe.h>
 #include <proto/proto_uxst.h>
 #include <proto/session.h>
@@ -173,7 +174,7 @@
 			    "wretr,wredis,"
 			    "status,weight,act,bck,"
 			    "chkfail,chkdown,lastchg,downtime,qlimit,"
-			    "pid,iid,sid,throttle,lbtot,tracked,type,"
+			    "pid,iid,sid,throttle,lbtot,tracked,type,rate,"
 			    "\n");
 }
 
@@ -650,21 +651,21 @@
 		if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
 			/* print a new table */
 			chunk_printf(&msg, sizeof(trash),
-				     "<table cols=\"26\" class=\"tbl\" width=\"100%%\">\n"
+				     "<table cols=\"27\" class=\"tbl\" width=\"100%%\">\n"
 				     "<tr align=\"center\" class=\"titre\">"
 				     "<th colspan=2 class=\"pxname\">%s</th>"
-				     "<th colspan=24 class=\"empty\"></th>"
+				     "<th colspan=25 class=\"empty\"></th>"
 				     "</tr>\n"
 				     "<tr align=\"center\" class=\"titre\">"
 				     "<th rowspan=2></th>"
-				     "<th colspan=3>Queue</th><th colspan=5>Sessions</th>"
+				     "<th colspan=3>Queue</th><th colspan=6>Sessions</th>"
 				     "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
 				     "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
 				     "<th colspan=8>Server</th>"
 				     "</tr>\n"
 				     "<tr align=\"center\" class=\"titre\">"
 				     "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
-				     "<th>Limit</th><th>Total</th><th>LbTot</th><th>In</th><th>Out</th>"
+				     "<th>Limit</th><th>Rate</th><th>Total</th><th>LbTot</th><th>In</th><th>Out</th>"
 				     "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
 				     "<th>Resp</th><th>Retr</th><th>Redis</th>"
 				     "<th>Status</th><th>Wght</th><th>Act</th>"
@@ -688,10 +689,10 @@
 				chunk_printf(&msg, sizeof(trash),
 				     /* name, queue */
 				     "<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=3></td>"
-				     /* sessions : current, max, limit, total, lbtot */
+				     /* sessions : current, max, limit, rate, total, lbtot */
 				     "<td align=right>%d</td><td align=right>%d</td>"
 				     "<td align=right>%d</td><td align=right>%d</td>"
-				     "<td align=right></td>"
+				     "<td align=right>%d</td><td align=right></td>"
 				     /* bytes : in, out */
 				     "<td align=right>%lld</td><td align=right>%lld</td>"
 				     /* denied: req, resp */
@@ -705,7 +706,8 @@
 				     /* rest of server: nothing */
 				     "<td align=center colspan=7></td></tr>"
 				     "",
-				     px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
+				     px->feconn, px->feconn_max, px->maxconn,
+				     read_freq_ctr(&px->fe_sess_per_sec), px->cum_feconn,
 				     px->bytes_in, px->bytes_out,
 				     px->denied_req, px->denied_resp,
 				     px->failed_req,
@@ -731,6 +733,8 @@
 				     ",,,,,,,,"
 				     /* pid, iid, sid, throttle, lbtot, tracked, type */
 				     "%d,%d,0,,,,%d,"
+				     /* rate */
+				     "%u,"
 				     "\n",
 				     px->id,
 				     px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
@@ -739,7 +743,8 @@
 				     px->failed_req,
 				     px->state == PR_STRUN ? "OPEN" :
 				     px->state == PR_STIDLE ? "FULL" : "STOP",
-				     relative_pid, px->uuid, STATS_TYPE_FE);
+				     relative_pid, px->uuid, STATS_TYPE_FE,
+				     read_freq_ctr(&px->fe_sess_per_sec));
 			}
 
 			if (buffer_write_chunk(rep, &msg) >= 0)
@@ -805,10 +810,10 @@
 				     "<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
 				     /* queue : current, max, limit */
 				     "<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td>"
-				     /* sessions : current, max, limit, total, lbtot */
+				     /* sessions : current, max, limit, rate, total, lbtot */
 				     "<td align=right>%d</td><td align=right>%d</td>"
 				     "<td align=right>%s</td><td align=right>%d</td>"
-				     "<td align=right>%d</td>"
+				     "<td align=right>%d</td><td align=right>%d</td>"
 				     /* bytes : in, out */
 				     "<td align=right>%lld</td><td align=right>%lld</td>"
 				     /* denied: req, resp */
@@ -822,6 +827,7 @@
 				     sv_state, sv->id,
 				     sv->nbpend, sv->nbpend_max, LIM2A0(sv->maxqueue, "-"),
 				     sv->cur_sess, sv->cur_sess_max, LIM2A1(sv->maxconn, "-"),
+				     read_freq_ctr(&sv->sess_per_sec),
 				     sv->cum_sess, sv->cum_lbconn,
 				     sv->bytes_in, sv->bytes_out,
 				     sv->failed_secu,
@@ -956,8 +962,14 @@
 				else
 					chunk_printf(&msg, sizeof(trash), ",");
 
-				/* type, then EOL */
-				chunk_printf(&msg, sizeof(trash), "%d,\n", STATS_TYPE_SV);
+				/* type */
+				chunk_printf(&msg, sizeof(trash), "%d,", STATS_TYPE_SV);
+
+				/* rate */
+				chunk_printf(&msg, sizeof(trash), "%u,", read_freq_ctr(&sv->sess_per_sec));
+
+				/* finish with EOL */
+				chunk_printf(&msg, sizeof(trash), "\n");
 			}
 			if (buffer_write_chunk(rep, &msg) >= 0)
 				return 0;
@@ -976,10 +988,10 @@
 				     "<tr align=center class=\"backend\"><td>Backend</td>"
 				     /* queue : current, max */
 				     "<td align=right>%d</td><td align=right>%d</td><td></td>"
-				     /* sessions : current, max, limit, total, lbtot */
+				     /* sessions : current, max, limit, rate, total, lbtot */
 				     "<td align=right>%d</td><td align=right>%d</td>"
 				     "<td align=right>%d</td><td align=right>%d</td>"
-				     "<td align=right>%d</td>"
+				     "<td align=right>%d</td><td align=right>%d</td>"
 				     /* bytes : in, out */
 				     "<td align=right>%lld</td><td align=right>%lld</td>"
 				     /* denied: req, resp */
@@ -995,7 +1007,9 @@
 				     "<td align=center nowrap>%s %s</td><td align=center>%d</td>"
 				     "<td align=center>%d</td><td align=center>%d</td>",
 				     px->nbpend /* or px->totpend ? */, px->nbpend_max,
-				     px->beconn, px->beconn_max, px->fullconn, px->cum_beconn, px->cum_lbconn,
+				     px->beconn, px->beconn_max, px->fullconn,
+				     read_freq_ctr(&px->be_sess_per_sec),
+				     px->cum_beconn, px->cum_lbconn,
 				     px->bytes_in, px->bytes_out,
 				     px->denied_req, px->denied_resp,
 				     px->failed_conns, px->failed_resp,
@@ -1040,6 +1054,8 @@
 				     ",%d,%d,%d,,"
 				     /* pid, iid, sid, throttle, lbtot, tracked, type */
 				     "%d,%d,0,,%d,,%d,"
+				     /* rate */
+				     "%u,"
 				     "\n",
 				     px->id,
 				     px->nbpend /* or px->totpend ? */, px->nbpend_max,
@@ -1054,7 +1070,8 @@
 				     px->down_trans, now.tv_sec - px->last_change,
 				     px->srv?be_downtime(px):0,
 				     relative_pid, px->uuid,
-				     px->cum_lbconn, STATS_TYPE_BE);
+				     px->cum_lbconn, STATS_TYPE_BE,
+				     read_freq_ctr(&px->be_sess_per_sec));
 			}
 			if (buffer_write_chunk(rep, &msg) >= 0)
 				return 0;
diff --git a/src/freq_ctr.c b/src/freq_ctr.c
new file mode 100644
index 0000000..4522ff3
--- /dev/null
+++ b/src/freq_ctr.c
@@ -0,0 +1,45 @@
+/*
+ * Event rate calculation functions.
+ *
+ * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <common/config.h>
+#include <common/standard.h>
+#include <common/time.h>
+#include <proto/freq_ctr.h>
+
+/* Read a frequency counter taking history into account for missing time in
+ * current period. Current second is sub-divided in 1000 chunks of one ms,
+ * and the missing ones are read proportionally from previous value. The
+ * return value has the same precision as one input data sample, so low rates
+ * will be inaccurate still appropriate for max checking. One trick we use for
+ * low values is to specially handle the case where the rate is between 0 and 1
+ * in order to avoid flapping while waiting for the next event.
+ */
+unsigned int read_freq_ctr(struct freq_ctr *ctr)
+{
+	unsigned int cur;
+	if (unlikely(ctr->curr_sec != now.tv_sec))
+		rotate_freq_ctr(ctr);
+
+	cur = ctr->curr_ctr;
+	if (ctr->prev_ctr <= 1 && !ctr->curr_ctr)
+		return ctr->prev_ctr; /* very low rate, avoid flapping */
+
+	return cur + mul32hi(ctr->prev_ctr, ~curr_sec_ms_scaled);
+}
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */
diff --git a/src/proto_http.c b/src/proto_http.c
index 6acb0b7..a8ade23 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -47,7 +47,9 @@
 #include <proto/hdr_idx.h>
 #include <proto/proto_tcp.h>
 #include <proto/proto_http.h>
+#include <proto/proxy.h>
 #include <proto/queue.h>
+#include <proto/server.h>
 #include <proto/session.h>
 #include <proto/stream_interface.h>
 #include <proto/stream_sock.h>
@@ -692,7 +694,7 @@
 
 	/* FIXME: we should increase a counter of redirects per server and per backend. */
 	if (s->srv)
-		s->srv->cum_sess++;
+		srv_inc_sess_ctr(s->srv);
 }
 
 /* Return the error message corresponding to si->err_type. It is assumed
@@ -1943,7 +1945,7 @@
 			s->be->beconn++;
 			if (s->be->beconn > s->be->beconn_max)
 				s->be->beconn_max = s->be->beconn;
-			s->be->cum_beconn++;
+			proxy_inc_be_ctr(s->be);
 			s->flags |= SN_BE_ASSIGNED;
 		}
 
@@ -2046,7 +2048,7 @@
 					s->be->beconn++;
 					if (s->be->beconn > s->be->beconn_max)
 						s->be->beconn_max = s->be->beconn;
-					s->be->cum_beconn++;
+					proxy_inc_be_ctr(s->be);
 
 					/* assign new parameters to the session from the new backend */
 					s->rep->rto = s->req->wto = s->be->timeout.server;
@@ -2067,7 +2069,7 @@
 			s->be->beconn++;
 			if (s->be->beconn > s->be->beconn_max)
 				s->be->beconn_max = s->be->beconn;
-			s->be->cum_beconn++;
+			proxy_inc_be_ctr(s->be);
 
 			/* assign new parameters to the session from the new backend */
 			s->rep->rto = s->req->wto = s->be->timeout.server;
@@ -2085,7 +2087,7 @@
 		s->be->beconn++;
 		if (s->be->beconn > s->be->beconn_max)
 			s->be->beconn_max = s->be->beconn;
-		s->be->cum_beconn++;
+		proxy_inc_be_ctr(s->be);
 		s->flags |= SN_BE_ASSIGNED;
 	}
 
diff --git a/src/session.c b/src/session.c
index 8ced9b8..b3c50ca 100644
--- a/src/session.c
+++ b/src/session.c
@@ -28,6 +28,7 @@
 #include <proto/proto_http.h>
 #include <proto/proto_tcp.h>
 #include <proto/queue.h>
+#include <proto/server.h>
 #include <proto/stream_interface.h>
 #include <proto/stream_sock.h>
 #include <proto/task.h>
@@ -367,7 +368,7 @@
 		if (conn_err == SN_ERR_NONE) {
 			/* state = SI_ST_CON now */
 			if (s->srv)
-				s->srv->cum_sess++;
+				srv_inc_sess_ctr(s->srv);
 			return;
 		}
 
@@ -381,7 +382,7 @@
 			}
 
 			if (s->srv)
-				s->srv->cum_sess++;
+				srv_inc_sess_ctr(s->srv);
 			if (s->srv)
 				s->srv->failed_conns++;
 			s->be->failed_conns++;