85afcf85e84a669bb538e96f8bac2ae7bd700857
[freeradius.git] / src / main / stats.c
1 /*
2  * stats.c      Internal statistics handling.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2008  The FreeRADIUS server project
21  * Copyright 2008  Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #ifdef WITH_STATS
31
32 #define USEC (1000000)
33 #define EMA_SCALE (100)
34 #define PREC (USEC * EMA_SCALE)
35
36 #define F_EMA_SCALE (1000000)
37
38 static struct timeval   start_time;
39 static struct timeval   hup_time;
40
41 fr_stats_t radius_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
42 #ifdef WITH_ACCOUNTING
43 fr_stats_t radius_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
44 #endif
45
46 #ifdef WITH_PROXY
47 fr_stats_t proxy_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
48 #ifdef WITH_ACCOUNTING
49 fr_stats_t proxy_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
50 #endif
51 #endif
52
53 void request_stats_final(REQUEST *request)
54 {
55         if (request->master_state == REQUEST_COUNTED) return;
56
57         if ((request->listener->type != RAD_LISTEN_NONE) &&
58             (request->listener->type != RAD_LISTEN_AUTH) &&
59             (request->listener->type != RAD_LISTEN_ACCT)) return;
60
61 #undef INC_AUTH
62 #define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;if (request->client && request->client->auth) request->client->auth->_x++;
63
64
65 #undef INC_ACCT
66 #define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;if (request->client && request->client->acct) request->client->acct->_x++
67
68         /*
69          *      Update the statistics.
70          *
71          *      Note that we do NOT do this in a child thread.
72          *      Instead, we update the stats when a request is
73          *      deleted, because only the main server thread calls
74          *      this function, which makes it thread-safe.
75          */
76         if (request->reply) switch (request->reply->code) {
77         case PW_AUTHENTICATION_ACK:
78                 INC_AUTH(total_responses);
79                 INC_AUTH(total_access_accepts);
80                 break;
81
82         case PW_AUTHENTICATION_REJECT:
83                 INC_AUTH(total_responses);
84                 INC_AUTH(total_access_rejects);
85                 break;
86
87         case PW_ACCESS_CHALLENGE:
88                 INC_AUTH(total_responses);
89                 INC_AUTH(total_access_challenges);
90                 break;
91
92 #ifdef WITH_ACCOUNTING
93         case PW_ACCOUNTING_RESPONSE:
94                 INC_ACCT(total_responses);
95                 break;
96 #endif
97
98                 /*
99                  *      No response, it must have been a bad
100                  *      authenticator.
101                  */
102         case 0:
103                 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
104                         if (request->reply->offset == -2) {
105                                 INC_AUTH(total_bad_authenticators);
106                         } else {
107                                 INC_AUTH(total_packets_dropped);
108                         }
109                 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
110                         if (request->reply->offset == -2) {
111                                 INC_ACCT(total_bad_authenticators);
112                         } else {
113                                 INC_ACCT(total_packets_dropped);
114                         }
115                 }
116                 break;
117
118         default:
119                 break;
120         }
121
122 #ifdef WITH_PROXY
123         if (!request->proxy || !request->proxy_listener) goto done;     /* simplifies formatting */
124
125         switch (request->proxy->code) {
126         case PW_AUTHENTICATION_REQUEST:
127                 proxy_auth_stats.total_requests += request->num_proxied_requests;
128                 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
129                 request->home_server->stats.total_requests += request->num_proxied_requests;
130                 break;
131
132 #ifdef WITH_ACCOUNTING
133         case PW_ACCOUNTING_REQUEST:
134                 proxy_acct_stats.total_requests++;
135                 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
136                 request->home_server->stats.total_requests += request->num_proxied_requests;
137                 break;
138 #endif
139
140         default:
141                 break;
142         }
143
144         if (!request->proxy_reply) goto done;   /* simplifies formatting */
145
146 #undef INC
147 #define INC(_x) proxy_auth_stats._x += request->num_proxied_responses; request->proxy_listener->stats._x += request->num_proxied_responses; request->home_server->stats._x += request->num_proxied_responses;
148
149         switch (request->proxy_reply->code) {
150         case PW_AUTHENTICATION_ACK:
151                 INC(total_responses);
152                 INC(total_access_accepts);
153                 break;
154
155         case PW_AUTHENTICATION_REJECT:
156                 INC(total_responses);
157                 INC(total_access_rejects);
158                 break;
159
160         case PW_ACCESS_CHALLENGE:
161                 INC(total_responses);
162                 INC(total_access_challenges);
163                 break;
164
165 #ifdef WITH_ACCOUNTING
166         case PW_ACCOUNTING_RESPONSE:
167                 proxy_acct_stats.total_responses++;
168                 request->proxy_listener->stats.total_responses++;
169                 request->home_server->stats.total_responses++;
170                 break;
171 #endif
172
173         default:
174                 proxy_auth_stats.total_unknown_types++;
175                 request->proxy_listener->stats.total_unknown_types++;
176                 request->home_server->stats.total_unknown_types++;
177                 break;
178         }
179
180  done:
181 #endif /* WITH_PROXY */
182
183         request->master_state = REQUEST_COUNTED;
184 }
185
186 typedef struct fr_stats2vp {
187         int     attribute;
188         size_t  offset;
189 } fr_stats2vp;
190
191 /*
192  *      Authentication
193  */
194 static fr_stats2vp authvp[] = {
195         { 128, offsetof(fr_stats_t, total_requests) },
196         { 129, offsetof(fr_stats_t, total_access_accepts) },
197         { 130, offsetof(fr_stats_t, total_access_rejects) },
198         { 131, offsetof(fr_stats_t, total_access_challenges) },
199         { 132, offsetof(fr_stats_t, total_responses) },
200         { 133, offsetof(fr_stats_t, total_dup_requests) },
201         { 134, offsetof(fr_stats_t, total_malformed_requests) },
202         { 135, offsetof(fr_stats_t, total_bad_authenticators) },
203         { 136, offsetof(fr_stats_t, total_packets_dropped) },
204         { 137, offsetof(fr_stats_t, total_unknown_types) },
205         { 0, 0 }
206 };
207
208
209 #ifdef WITH_PROXY
210 /*
211  *      Proxied authentication requests.
212  */
213 static fr_stats2vp proxy_authvp[] = {
214         { 138, offsetof(fr_stats_t, total_requests) },
215         { 139, offsetof(fr_stats_t, total_access_accepts) },
216         { 140, offsetof(fr_stats_t, total_access_rejects) },
217         { 141, offsetof(fr_stats_t, total_access_challenges) },
218         { 142, offsetof(fr_stats_t, total_responses) },
219         { 143, offsetof(fr_stats_t, total_dup_requests) },
220         { 144, offsetof(fr_stats_t, total_malformed_requests) },
221         { 145, offsetof(fr_stats_t, total_bad_authenticators) },
222         { 146, offsetof(fr_stats_t, total_packets_dropped) },
223         { 147, offsetof(fr_stats_t, total_unknown_types) },
224         { 0, 0 }
225 };
226 #endif
227
228
229 #ifdef WITH_ACCOUNTING
230 /*
231  *      Accounting
232  */
233 static fr_stats2vp acctvp[] = {
234         { 148, offsetof(fr_stats_t, total_requests) },
235         { 149, offsetof(fr_stats_t, total_responses) },
236         { 150, offsetof(fr_stats_t, total_dup_requests) },
237         { 151, offsetof(fr_stats_t, total_malformed_requests) },
238         { 152, offsetof(fr_stats_t, total_bad_authenticators) },
239         { 153, offsetof(fr_stats_t, total_packets_dropped) },
240         { 154, offsetof(fr_stats_t, total_unknown_types) },
241         { 0, 0 }
242 };
243
244 #ifdef WITH_PROXY
245 static fr_stats2vp proxy_acctvp[] = {
246         { 155, offsetof(fr_stats_t, total_requests) },
247         { 156, offsetof(fr_stats_t, total_responses) },
248         { 157, offsetof(fr_stats_t, total_dup_requests) },
249         { 158, offsetof(fr_stats_t, total_malformed_requests) },
250         { 159, offsetof(fr_stats_t, total_bad_authenticators) },
251         { 160, offsetof(fr_stats_t, total_packets_dropped) },
252         { 161, offsetof(fr_stats_t, total_unknown_types) },
253         { 0, 0 }
254 };
255 #endif
256 #endif
257
258 static fr_stats2vp client_authvp[] = {
259         { 128, offsetof(fr_stats_t, total_requests) },
260         { 129, offsetof(fr_stats_t, total_access_accepts) },
261         { 130, offsetof(fr_stats_t, total_access_rejects) },
262         { 131, offsetof(fr_stats_t, total_access_challenges) },
263         { 132, offsetof(fr_stats_t, total_responses) },
264         { 133, offsetof(fr_stats_t, total_dup_requests) },
265         { 134, offsetof(fr_stats_t, total_malformed_requests) },
266         { 135, offsetof(fr_stats_t, total_bad_authenticators) },
267         { 136, offsetof(fr_stats_t, total_packets_dropped) },
268         { 137, offsetof(fr_stats_t, total_unknown_types) },
269         { 0, 0 }
270 };
271
272 #ifdef WITH_ACCOUNTING
273 static fr_stats2vp client_acctvp[] = {
274         { 148, offsetof(fr_stats_t, total_requests) },
275         { 149, offsetof(fr_stats_t, total_responses) },
276         { 150, offsetof(fr_stats_t, total_dup_requests) },
277         { 151, offsetof(fr_stats_t, total_malformed_requests) },
278         { 152, offsetof(fr_stats_t, total_bad_authenticators) },
279         { 153, offsetof(fr_stats_t, total_packets_dropped) },
280         { 154, offsetof(fr_stats_t, total_unknown_types) },
281         { 0, 0 }
282 };
283 #endif
284
285 static void request_stats_addvp(REQUEST *request,
286                                 fr_stats2vp *table, fr_stats_t *stats)
287 {
288         int i;
289         VALUE_PAIR *vp;
290
291         for (i = 0; table[i].attribute != 0; i++) {
292                 vp = radius_paircreate(request, &request->reply->vps,
293                                        table[i].attribute, VENDORPEC_FREERADIUS,
294                                        PW_TYPE_INTEGER);
295                 if (!vp) continue;
296
297                 vp->vp_integer = *(int *)(((char *) stats) + table[i].offset);
298         }
299 }
300
301
302 void request_stats_reply(REQUEST *request)
303 {
304         VALUE_PAIR *flag, *vp;
305
306         /*
307          *      Statistics are available ONLY on a "status" port.
308          */
309         rad_assert(request->packet->code == PW_STATUS_SERVER);
310         rad_assert(request->listener->type == RAD_LISTEN_NONE);
311                 
312         flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS);
313         if (!flag || (flag->vp_integer == 0)) return;
314
315         /*
316          *      Authentication.
317          */
318         if (((flag->vp_integer & 0x01) != 0) &&
319             ((flag->vp_integer & 0xc0) == 0)) {
320                 request_stats_addvp(request, authvp, &radius_auth_stats);
321         }
322                 
323 #ifdef WITH_ACCOUNTING
324         /*
325          *      Accounting
326          */
327         if (((flag->vp_integer & 0x02) != 0) &&
328             ((flag->vp_integer & 0xc0) == 0)) {
329                 request_stats_addvp(request, acctvp, &radius_acct_stats);
330         }
331 #endif
332
333 #ifdef WITH_PROXY
334         /*
335          *      Proxied authentication requests.
336          */
337         if (((flag->vp_integer & 0x04) != 0) &&
338             ((flag->vp_integer & 0x20) == 0)) {
339                 request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
340         }
341
342 #ifdef WITH_ACCOUNTING
343         /*
344          *      Proxied accounting requests.
345          */
346         if (((flag->vp_integer & 0x08) != 0) &&
347             ((flag->vp_integer & 0x20) == 0)) {
348                 request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
349         }
350 #endif
351 #endif
352
353         /*
354          *      Internal server statistics
355          */
356         if ((flag->vp_integer & 0x10) != 0) {
357                 vp = radius_paircreate(request, &request->reply->vps,
358                                        176, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
359                 if (vp) vp->vp_date = start_time.tv_sec;
360                 vp = radius_paircreate(request, &request->reply->vps,
361                                        177, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
362                 if (vp) vp->vp_date = hup_time.tv_sec;
363                 
364 #ifdef HAVE_PTHREAD_H
365                 int i, array[RAD_LISTEN_MAX];
366
367                 thread_pool_queue_stats(array);
368
369                 for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
370                         vp = radius_paircreate(request, &request->reply->vps,
371                                                162 + i, VENDORPEC_FREERADIUS,
372                                                PW_TYPE_INTEGER);
373                         
374                         if (!vp) continue;
375                         vp->vp_integer = array[i];
376                 }
377 #endif
378         }
379
380         /*
381          *      For a particular client.
382          */
383         if ((flag->vp_integer & 0x20) != 0) {
384                 fr_ipaddr_t ipaddr;
385                 VALUE_PAIR *server_ip, *server_port = NULL;
386                 RADCLIENT *client = NULL;
387                 RADCLIENT_LIST *cl = NULL;
388
389                 /*
390                  *      See if we need to look up the client by server
391                  *      socket.
392                  */
393                 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
394                 if (server_ip) {
395                         server_port = pairfind(request->packet->vps,
396                                                171, VENDORPEC_FREERADIUS);
397
398                         if (server_port) {
399                                 ipaddr.af = AF_INET;
400                                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
401                                 cl = listener_find_client_list(&ipaddr, server_port->vp_integer);
402                                                                
403                                 /*
404                                  *      Not found: don't do anything
405                                  */
406                                 if (!cl) return;
407                         }
408                 }
409
410
411                 vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS);
412                 if (vp) {
413                         ipaddr.af = AF_INET;
414                         ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
415                         client = client_find(cl, &ipaddr, IPPROTO_UDP);
416 #ifdef WITH_TCP
417                         if (!client) {
418                                 client = client_find(cl, &ipaddr, IPPROTO_TCP);
419                         }
420 #endif
421
422                         /*
423                          *      Else look it up by number.
424                          */
425                 } else if ((vp = pairfind(request->packet->vps,
426                                            168, VENDORPEC_FREERADIUS)) != NULL) {
427                         client = client_findbynumber(cl, vp->vp_integer);
428                 }
429
430                 if (client) {
431                         /*
432                          *      If found, echo it back, along with
433                          *      the requested statistics.
434                          */
435                         pairadd(&request->reply->vps, paircopyvp(vp));
436
437                         /*
438                          *      When retrieving client by number, also
439                          *      echo back it's IP address.
440                          */
441                         if ((vp->type == PW_TYPE_INTEGER) &&
442                             (client->ipaddr.af == AF_INET)) {
443                                 vp = radius_paircreate(request,
444                                                        &request->reply->vps,
445                                                        167, VENDORPEC_FREERADIUS,
446                                                        PW_TYPE_IPADDR);
447                                 if (vp) {
448                                         vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
449                                 }
450
451                                 if (client->prefix != 32) {
452                                         vp = radius_paircreate(request,
453                                                                &request->reply->vps,
454                                                                169, VENDORPEC_FREERADIUS,
455                                                                PW_TYPE_INTEGER);
456                                         if (vp) {
457                                                 vp->vp_integer = client->prefix;
458                                         }
459                                 }
460                         }
461                         
462                         if (server_ip) {
463                                 pairadd(&request->reply->vps,
464                                         paircopyvp(server_ip));
465                                 pairadd(&request->reply->vps,
466                                         paircopyvp(server_port));
467                         }
468
469                         if (client->auth &&
470                             ((flag->vp_integer & 0x01) != 0)) {
471                                 request_stats_addvp(request, client_authvp,
472                                                     client->auth);
473                         }
474 #ifdef WITH_ACCOUNTING
475                         if (client->acct &&
476                             ((flag->vp_integer & 0x01) != 0)) {
477                                 request_stats_addvp(request, client_acctvp,
478                                                     client->acct);
479                         }
480 #endif
481                 } /* else client wasn't found, don't echo it back */
482         }
483
484         /*
485          *      For a particular "listen" socket.
486          */
487         if (((flag->vp_integer & 0x40) != 0) &&
488             ((flag->vp_integer & 0x03) != 0)) {
489                 rad_listen_t *this;
490                 VALUE_PAIR *server_ip, *server_port;
491                 fr_ipaddr_t ipaddr;
492
493                 /*
494                  *      See if we need to look up the server by socket
495                  *      socket.
496                  */
497                 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
498                 if (!server_ip) return;
499
500                 server_port = pairfind(request->packet->vps,
501                                        171, VENDORPEC_FREERADIUS);
502                 if (!server_port) return;
503                 
504                 ipaddr.af = AF_INET;
505                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
506                 this = listener_find_byipaddr(&ipaddr,
507                                               server_port->vp_integer);
508                 
509                 /*
510                  *      Not found: don't do anything
511                  */
512                 if (!this) return;
513                 
514                 pairadd(&request->reply->vps,
515                         paircopyvp(server_ip));
516                 pairadd(&request->reply->vps,
517                         paircopyvp(server_port));
518
519                 if (((flag->vp_integer & 0x01) != 0) &&
520                     ((request->listener->type == RAD_LISTEN_AUTH) ||
521                      (request->listener->type == RAD_LISTEN_NONE))) {
522                         request_stats_addvp(request, authvp, &this->stats);
523                 }
524                 
525 #ifdef WITH_ACCOUNTING
526                 if (((flag->vp_integer & 0x02) != 0) &&
527                     ((request->listener->type == RAD_LISTEN_ACCT) ||
528                      (request->listener->type == RAD_LISTEN_NONE))) {
529                         request_stats_addvp(request, acctvp, &this->stats);
530                 }
531 #endif
532         }
533
534 #ifdef WITH_PROXY
535         /*
536          *      Home servers.
537          */
538         if (((flag->vp_integer & 0x80) != 0) &&
539             ((flag->vp_integer & 0x03) != 0)) {
540                 home_server *home;
541                 VALUE_PAIR *server_ip, *server_port;
542                 fr_ipaddr_t ipaddr;
543
544                 /*
545                  *      See if we need to look up the server by socket
546                  *      socket.
547                  */
548                 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
549                 if (!server_ip) return;
550
551                 server_port = pairfind(request->packet->vps,
552                                        171, VENDORPEC_FREERADIUS);
553                 if (!server_port) return;
554                 
555                 ipaddr.af = AF_INET;
556                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
557                 home = home_server_find(&ipaddr, server_port->vp_integer,
558                                         IPPROTO_UDP);
559
560                 /*
561                  *      Not found: don't do anything
562                  */
563                 if (!home) return;
564                 
565                 pairadd(&request->reply->vps,
566                         paircopyvp(server_ip));
567                 pairadd(&request->reply->vps,
568                         paircopyvp(server_port));
569
570                 vp = radius_paircreate(request, &request->reply->vps,
571                                        172, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
572                 if (vp) vp->vp_integer = home->currently_outstanding;
573
574                 vp = radius_paircreate(request, &request->reply->vps,
575                                        173, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
576                 if (vp) vp->vp_integer = home->state;
577
578                 if ((home->state == HOME_STATE_ALIVE) &&
579                     (home->revive_time.tv_sec != 0)) {
580                         vp = radius_paircreate(request, &request->reply->vps,
581                                                175, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
582                         if (vp) vp->vp_date = home->revive_time.tv_sec;
583                 }
584
585                 if ((home->state == HOME_STATE_ALIVE) &&
586                     (home->ema.window > 0)) {
587                                 vp = radius_paircreate(request,
588                                                        &request->reply->vps,
589                                                        178, VENDORPEC_FREERADIUS,
590                                                        PW_TYPE_INTEGER);
591                                 if (vp) vp->vp_integer = home->ema.window;
592                                 vp = radius_paircreate(request,
593                                                        &request->reply->vps,
594                                                        179, VENDORPEC_FREERADIUS,
595                                                        PW_TYPE_INTEGER);
596                                 if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
597                                 vp = radius_paircreate(request,
598                                                        &request->reply->vps,
599                                                        180, VENDORPEC_FREERADIUS,
600                                                        PW_TYPE_INTEGER);
601                                 if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
602
603                 }
604
605                 if (home->state == HOME_STATE_IS_DEAD) {
606                         vp = radius_paircreate(request, &request->reply->vps,
607                                                174, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
608                         if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
609                 }
610
611                 if (((flag->vp_integer & 0x01) != 0) &&
612                     (home->type == HOME_TYPE_AUTH)) {
613                         request_stats_addvp(request, proxy_authvp,
614                                             &home->stats);
615                 }
616
617 #ifdef WITH_ACCOUNTING
618                 if (((flag->vp_integer & 0x02) != 0) &&
619                     (home->type == HOME_TYPE_ACCT)) {
620                         request_stats_addvp(request, proxy_acctvp,
621                                             &home->stats);
622                 }
623 #endif
624         }
625 #endif  /* WITH_PROXY */
626 }
627
628 void radius_stats_init(int flag)
629 {
630         if (!flag) {
631                 gettimeofday(&start_time, NULL);
632                 hup_time = start_time; /* it's just nicer this way */
633         } else {
634                 gettimeofday(&hup_time, NULL);
635         }
636 }
637
638 void radius_stats_ema(fr_stats_ema_t *ema,
639                       struct timeval *start, struct timeval *end)
640 {
641         int micro;
642         time_t tdiff;
643 #ifdef WITH_STATS_DEBUG
644         static int n = 0;
645 #endif
646         if (ema->window == 0) return;
647
648         rad_assert(start->tv_sec >= end->tv_sec);
649
650         /*
651          *      Initialize it.
652          */
653         if (ema->f1 == 0) {
654                 if (ema->window > 10000) ema->window = 10000;
655                 
656                 ema->f1 =  (2 * F_EMA_SCALE) / (ema->window + 1);
657                 ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
658         }
659
660
661         tdiff = start->tv_sec;
662         tdiff -= end->tv_sec;
663         
664         micro = (int) tdiff;
665         if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
666         micro *= USEC;
667         micro += start->tv_usec;
668         micro -= end->tv_usec;
669         
670         micro *= EMA_SCALE;
671
672         if (ema->ema1 == 0) {
673                 ema->ema1 = micro;
674                 ema->ema10 = micro;
675         } else {
676                 int diff;
677                 
678                 diff = ema->f1 * (micro - ema->ema1);
679                 ema->ema1 += (diff / 1000000);
680                 
681                 diff = ema->f10 * (micro - ema->ema10);
682                 ema->ema10 += (diff / 1000000);
683         }
684         
685         
686 #ifdef WITH_STATS_DEBUG
687         DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
688               n, micro / PREC, (micro / EMA_SCALE) % USEC,
689               ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
690               ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
691         n++;
692 #endif  
693 }
694
695 #endif /* WITH_STATS */