MEDIUM: session: maintain per-backend and per-server time statistics

Using the last rate counters, we now compute the queue, connect, response
and total times per server and per backend with a 95% accuracy over the last
1024 samples. The operation is cheap so we don't need to condition it.
diff --git a/include/common/defaults.h b/include/common/defaults.h
index bdd75cf..8d5d62a 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -219,4 +219,15 @@
 #define SSL_DEFAULT_DH_PARAM 0
 #endif
 
+/* Number of samples used to compute the times reported in stats. A power of
+ * two is highly recommended, and this value multiplied by the largest response
+ * time must not overflow and unsigned int. See freq_ctr.h for more information.
+ * We consider that values are accurate to 95% with two batches of samples below,
+ * so in order to advertise accurate times across 1k samples, we effectively
+ * measure over 512.
+ */
+#ifndef TIME_STATS_SAMPLES
+#define TIME_STATS_SAMPLES 512
+#endif
+
 #endif /* _COMMON_DEFAULTS_H */
diff --git a/include/proto/session.h b/include/proto/session.h
index fc83989..c835bf0 100644
--- a/include/proto/session.h
+++ b/include/proto/session.h
@@ -50,6 +50,9 @@
 			 struct track_ctr_prm *prm,
 			 struct proxy *defpx, char **err);
 
+/* Update the session's backend and server time stats */
+void session_update_time_stats(struct session *s);
+
 /* returns the session from a void *owner */
 static inline struct session *session_from_task(struct task *t)
 {
diff --git a/include/types/counters.h b/include/types/counters.h
index ecdc7cb..172f8a6 100644
--- a/include/types/counters.h
+++ b/include/types/counters.h
@@ -3,7 +3,7 @@
  * This file contains structure declarations for statistics counters.
  *
  * Copyright 2008-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
- * Copyright 2011 Willy Tarreau <w@1wt.eu>
+ * Copyright 2011-2014 Willy Tarreau <w@1wt.eu>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -55,6 +55,8 @@
 	long long redispatches;                 /* retried and redispatched connections (BE only) */
 	long long intercepted_req;              /* number of monitoring or stats requests intercepted by the frontend */
 
+	unsigned int q_time, c_time, d_time, t_time; /* sums of conn_time, queue_time, data_time, total_time */
+
 	union {
 		struct {
 			long long cum_req;      /* cumulated number of processed HTTP requests */
@@ -96,6 +98,8 @@
 	long long retries, redispatches;	/* retried and redispatched connections */
 	long long failed_secu;			/* blocked responses because of security concerns */
 
+	unsigned int q_time, c_time, d_time, t_time; /* sums of conn_time, queue_time, data_time, total_time */
+
 	union {
 		struct {
 			long long rsp[6];	/* http response codes */
diff --git a/src/proto_http.c b/src/proto_http.c
index 082f161..48dbc43 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -4714,6 +4714,8 @@
 		s->do_log(s);
 	}
 
+	session_update_time_stats(s);
+
 	s->logs.accept_date = date; /* user-visible date for logging */
 	s->logs.tv_accept = now;  /* corrected date for internal use */
 	tv_zero(&s->logs.tv_request);
diff --git a/src/session.c b/src/session.c
index 4412125..f828d9c 100644
--- a/src/session.c
+++ b/src/session.c
@@ -2600,6 +2600,9 @@
 		s->do_log(s);
 	}
 
+	/* update time stats for this session */
+	session_update_time_stats(s);
+
 	/* the task MUST not be in the run queue anymore */
 	session_free(s);
 	task_delete(t);
@@ -2607,6 +2610,48 @@
 	return NULL;
 }
 
+/* Update the session's backend and server time stats */
+void session_update_time_stats(struct session *s)
+{
+	int t_request;
+	int t_queue;
+	int t_connect;
+	int t_data;
+	int t_close;
+	struct server *srv;
+
+	t_request = 0;
+	t_queue   = s->logs.t_queue;
+	t_connect = s->logs.t_connect;
+	t_close   = s->logs.t_close;
+	t_data    = s->logs.t_data;
+
+	if (s->be->mode != PR_MODE_HTTP)
+		t_data = t_connect;
+
+	if (t_connect < 0 || t_data < 0)
+		return;
+
+	if (tv_isge(&s->logs.tv_request, &s->logs.tv_accept))
+		t_request = tv_ms_elapsed(&s->logs.tv_accept, &s->logs.tv_request);
+
+	t_data    -= t_connect;
+	t_connect -= t_queue;
+	t_queue   -= t_request;
+
+	srv = objt_server(s->target);
+	if (srv) {
+		swrate_add(&srv->counters.q_time, TIME_STATS_SAMPLES, t_queue);
+		swrate_add(&srv->counters.c_time, TIME_STATS_SAMPLES, t_connect);
+		swrate_add(&srv->counters.d_time, TIME_STATS_SAMPLES, t_data);
+		swrate_add(&srv->counters.t_time, TIME_STATS_SAMPLES, t_close);
+	}
+	swrate_add(&s->be->be_counters.q_time, TIME_STATS_SAMPLES, t_queue);
+	swrate_add(&s->be->be_counters.c_time, TIME_STATS_SAMPLES, t_connect);
+	swrate_add(&s->be->be_counters.d_time, TIME_STATS_SAMPLES, t_data);
+	swrate_add(&s->be->be_counters.t_time, TIME_STATS_SAMPLES, t_close);
+}
+
 /*
  * This function adjusts sess->srv_conn and maintains the previous and new
  * server's served session counts. Setting newsrv to NULL is enough to release