2 * stats.c Internal statistics handling.
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.
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.
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
20 * Copyright 2008 The FreeRADIUS server project
21 * Copyright 2008 Alan DeKok <aland@deployingradius.com>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/rad_assert.h>
31 #define USEC (1000000)
32 #define EMA_SCALE (100)
33 #define PREC (USEC * EMA_SCALE)
35 #define F_EMA_SCALE (1000000)
37 static struct timeval start_time;
38 static struct timeval hup_time;
40 #define FR_STATS_INIT { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
41 { 0, 0, 0, 0, 0, 0, 0, 0 }}
43 fr_stats_t radius_auth_stats = FR_STATS_INIT;
44 #ifdef WITH_ACCOUNTING
45 fr_stats_t radius_acct_stats = FR_STATS_INIT;
48 fr_stats_t radius_coa_stats = FR_STATS_INIT;
49 fr_stats_t radius_dsc_stats = FR_STATS_INIT;
53 fr_stats_t proxy_auth_stats = FR_STATS_INIT;
54 #ifdef WITH_ACCOUNTING
55 fr_stats_t proxy_acct_stats = FR_STATS_INIT;
58 fr_stats_t proxy_coa_stats = FR_STATS_INIT;
59 fr_stats_t proxy_dsc_stats = FR_STATS_INIT;
64 static void tv_sub(struct timeval *end, struct timeval *start,
65 struct timeval *elapsed)
67 elapsed->tv_sec = end->tv_sec - start->tv_sec;
68 if (elapsed->tv_sec > 0) {
70 elapsed->tv_usec = USEC;
74 elapsed->tv_usec += end->tv_usec;
75 elapsed->tv_usec -= start->tv_usec;
77 if (elapsed->tv_usec >= USEC) {
78 elapsed->tv_usec -= USEC;
83 static void stats_time(fr_stats_t *stats, struct timeval *start,
89 if ((start->tv_sec == 0) || (end->tv_sec == 0) ||
90 (end->tv_sec < start->tv_sec)) return;
92 tv_sub(end, start, &diff);
94 if (diff.tv_sec >= 10) {
100 delay = (diff.tv_sec * USEC) + diff.tv_usec;
103 for (i = 0; i < 7; i++) {
113 void request_stats_final(REQUEST *request)
115 if (request->master_state == REQUEST_COUNTED) return;
117 if (!request->listener) return;
118 if (!request->client) return;
120 if ((request->listener->type != RAD_LISTEN_NONE) &&
121 #ifdef WITH_ACCOUNTING
122 (request->listener->type != RAD_LISTEN_ACCT) &&
125 (request->listener->type != RAD_LISTEN_COA) &&
127 (request->listener->type != RAD_LISTEN_AUTH)) return;
129 /* don't count statistic requests */
130 if (request->packet->code == PW_CODE_STATUS_SERVER)
134 #define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;request->client->auth._x++;
138 #ifdef WITH_ACCOUNTING
139 #define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;request->client->acct._x++
146 #define INC_COA(_x) radius_coa_stats._x++;request->listener->stats._x++;request->client->coa._x++
153 #define INC_DSC(_x) radius_dsc_stats._x++;request->listener->stats._x++;request->client->dsc._x++
159 * Update the statistics.
161 * Note that we do NOT do this in a child thread.
162 * Instead, we update the stats when a request is
163 * deleted, because only the main server thread calls
164 * this function, which makes it thread-safe.
166 if (request->reply && (request->packet->code != PW_CODE_STATUS_SERVER)) switch (request->reply->code) {
167 case PW_CODE_ACCESS_ACCEPT:
168 INC_AUTH(total_access_accepts);
171 INC_AUTH(total_responses);
174 * FIXME: Do the time calculations once...
176 stats_time(&radius_auth_stats,
177 &request->packet->timestamp,
178 &request->reply->timestamp);
179 stats_time(&request->client->auth,
180 &request->packet->timestamp,
181 &request->reply->timestamp);
182 stats_time(&request->listener->stats,
183 &request->packet->timestamp,
184 &request->reply->timestamp);
187 case PW_CODE_ACCESS_REJECT:
188 INC_AUTH(total_access_rejects);
191 case PW_CODE_ACCESS_CHALLENGE:
192 INC_AUTH(total_access_challenges);
195 #ifdef WITH_ACCOUNTING
196 case PW_CODE_ACCOUNTING_RESPONSE:
197 INC_ACCT(total_responses);
198 stats_time(&radius_acct_stats,
199 &request->packet->timestamp,
200 &request->reply->timestamp);
201 stats_time(&request->client->acct,
202 &request->packet->timestamp,
203 &request->reply->timestamp);
208 case PW_CODE_COA_ACK:
209 INC_COA(total_access_accepts);
211 INC_COA(total_responses);
212 stats_time(&request->client->coa,
213 &request->packet->timestamp,
214 &request->reply->timestamp);
217 case PW_CODE_COA_NAK:
218 INC_COA(total_access_rejects);
221 case PW_CODE_DISCONNECT_ACK:
222 INC_DSC(total_access_accepts);
224 INC_DSC(total_responses);
225 stats_time(&request->client->dsc,
226 &request->packet->timestamp,
227 &request->reply->timestamp);
230 case PW_CODE_DISCONNECT_NAK:
231 INC_DSC(total_access_rejects);
236 * No response, it must have been a bad
240 if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
241 if (request->reply->offset == -2) {
242 INC_AUTH(total_bad_authenticators);
244 INC_AUTH(total_packets_dropped);
246 } else if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
247 if (request->reply->offset == -2) {
248 INC_ACCT(total_bad_authenticators);
250 INC_ACCT(total_packets_dropped);
260 if (!request->proxy || !request->proxy_listener) goto done; /* simplifies formatting */
262 switch (request->proxy->code) {
263 case PW_CODE_ACCESS_REQUEST:
264 proxy_auth_stats.total_requests += request->num_proxied_requests;
265 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
266 request->home_server->stats.total_requests += request->num_proxied_requests;
269 #ifdef WITH_ACCOUNTING
270 case PW_CODE_ACCOUNTING_REQUEST:
271 proxy_acct_stats.total_requests++;
272 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
273 request->home_server->stats.total_requests += request->num_proxied_requests;
281 if (!request->proxy_reply) goto done; /* simplifies formatting */
284 #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;
286 switch (request->proxy_reply->code) {
287 case PW_CODE_ACCESS_ACCEPT:
288 INC(total_access_accepts);
290 INC(total_responses);
291 stats_time(&proxy_auth_stats,
292 &request->proxy->timestamp,
293 &request->proxy_reply->timestamp);
294 stats_time(&request->home_server->stats,
295 &request->proxy->timestamp,
296 &request->proxy_reply->timestamp);
299 case PW_CODE_ACCESS_REJECT:
300 INC(total_access_rejects);
303 case PW_CODE_ACCESS_CHALLENGE:
304 INC(total_access_challenges);
307 #ifdef WITH_ACCOUNTING
308 case PW_CODE_ACCOUNTING_RESPONSE:
309 proxy_acct_stats.total_responses++;
310 request->proxy_listener->stats.total_responses++;
311 request->home_server->stats.total_responses++;
312 stats_time(&proxy_acct_stats,
313 &request->proxy->timestamp,
314 &request->proxy_reply->timestamp);
315 stats_time(&request->home_server->stats,
316 &request->proxy->timestamp,
317 &request->proxy_reply->timestamp);
322 proxy_auth_stats.total_unknown_types++;
323 request->proxy_listener->stats.total_unknown_types++;
324 request->home_server->stats.total_unknown_types++;
329 #endif /* WITH_PROXY */
331 request->master_state = REQUEST_COUNTED;
334 typedef struct fr_stats2vp {
342 static fr_stats2vp authvp[] = {
343 { 128, offsetof(fr_stats_t, total_requests) },
344 { 129, offsetof(fr_stats_t, total_access_accepts) },
345 { 130, offsetof(fr_stats_t, total_access_rejects) },
346 { 131, offsetof(fr_stats_t, total_access_challenges) },
347 { 132, offsetof(fr_stats_t, total_responses) },
348 { 133, offsetof(fr_stats_t, total_dup_requests) },
349 { 134, offsetof(fr_stats_t, total_malformed_requests) },
350 { 135, offsetof(fr_stats_t, total_bad_authenticators) },
351 { 136, offsetof(fr_stats_t, total_packets_dropped) },
352 { 137, offsetof(fr_stats_t, total_unknown_types) },
359 * Proxied authentication requests.
361 static fr_stats2vp proxy_authvp[] = {
362 { 138, offsetof(fr_stats_t, total_requests) },
363 { 139, offsetof(fr_stats_t, total_access_accepts) },
364 { 140, offsetof(fr_stats_t, total_access_rejects) },
365 { 141, offsetof(fr_stats_t, total_access_challenges) },
366 { 142, offsetof(fr_stats_t, total_responses) },
367 { 143, offsetof(fr_stats_t, total_dup_requests) },
368 { 144, offsetof(fr_stats_t, total_malformed_requests) },
369 { 145, offsetof(fr_stats_t, total_bad_authenticators) },
370 { 146, offsetof(fr_stats_t, total_packets_dropped) },
371 { 147, offsetof(fr_stats_t, total_unknown_types) },
377 #ifdef WITH_ACCOUNTING
381 static fr_stats2vp acctvp[] = {
382 { 148, offsetof(fr_stats_t, total_requests) },
383 { 149, offsetof(fr_stats_t, total_responses) },
384 { 150, offsetof(fr_stats_t, total_dup_requests) },
385 { 151, offsetof(fr_stats_t, total_malformed_requests) },
386 { 152, offsetof(fr_stats_t, total_bad_authenticators) },
387 { 153, offsetof(fr_stats_t, total_packets_dropped) },
388 { 154, offsetof(fr_stats_t, total_unknown_types) },
393 static fr_stats2vp proxy_acctvp[] = {
394 { 155, offsetof(fr_stats_t, total_requests) },
395 { 156, offsetof(fr_stats_t, total_responses) },
396 { 157, offsetof(fr_stats_t, total_dup_requests) },
397 { 158, offsetof(fr_stats_t, total_malformed_requests) },
398 { 159, offsetof(fr_stats_t, total_bad_authenticators) },
399 { 160, offsetof(fr_stats_t, total_packets_dropped) },
400 { 161, offsetof(fr_stats_t, total_unknown_types) },
406 static fr_stats2vp client_authvp[] = {
407 { 128, offsetof(fr_stats_t, total_requests) },
408 { 129, offsetof(fr_stats_t, total_access_accepts) },
409 { 130, offsetof(fr_stats_t, total_access_rejects) },
410 { 131, offsetof(fr_stats_t, total_access_challenges) },
411 { 132, offsetof(fr_stats_t, total_responses) },
412 { 133, offsetof(fr_stats_t, total_dup_requests) },
413 { 134, offsetof(fr_stats_t, total_malformed_requests) },
414 { 135, offsetof(fr_stats_t, total_bad_authenticators) },
415 { 136, offsetof(fr_stats_t, total_packets_dropped) },
416 { 137, offsetof(fr_stats_t, total_unknown_types) },
420 #ifdef WITH_ACCOUNTING
421 static fr_stats2vp client_acctvp[] = {
422 { 148, offsetof(fr_stats_t, total_requests) },
423 { 149, offsetof(fr_stats_t, total_responses) },
424 { 150, offsetof(fr_stats_t, total_dup_requests) },
425 { 151, offsetof(fr_stats_t, total_malformed_requests) },
426 { 152, offsetof(fr_stats_t, total_bad_authenticators) },
427 { 153, offsetof(fr_stats_t, total_packets_dropped) },
428 { 154, offsetof(fr_stats_t, total_unknown_types) },
433 static void request_stats_addvp(REQUEST *request,
434 fr_stats2vp *table, fr_stats_t *stats)
440 for (i = 0; table[i].attribute != 0; i++) {
441 vp = radius_paircreate(request->reply, &request->reply->vps,
442 table[i].attribute, VENDORPEC_FREERADIUS);
445 counter = *(fr_uint_t *) (((uint8_t *) stats) + table[i].offset);
446 vp->vp_integer = counter;
451 void request_stats_reply(REQUEST *request)
453 VALUE_PAIR *flag, *vp;
456 * Statistics are available ONLY on a "status" port.
458 rad_assert(request->packet->code == PW_CODE_STATUS_SERVER);
459 rad_assert(request->listener->type == RAD_LISTEN_NONE);
461 flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS, TAG_ANY);
462 if (!flag || (flag->vp_integer == 0)) return;
467 if (((flag->vp_integer & 0x01) != 0) &&
468 ((flag->vp_integer & 0xc0) == 0)) {
469 request_stats_addvp(request, authvp, &radius_auth_stats);
472 #ifdef WITH_ACCOUNTING
476 if (((flag->vp_integer & 0x02) != 0) &&
477 ((flag->vp_integer & 0xc0) == 0)) {
478 request_stats_addvp(request, acctvp, &radius_acct_stats);
484 * Proxied authentication requests.
486 if (((flag->vp_integer & 0x04) != 0) &&
487 ((flag->vp_integer & 0x20) == 0)) {
488 request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
491 #ifdef WITH_ACCOUNTING
493 * Proxied accounting requests.
495 if (((flag->vp_integer & 0x08) != 0) &&
496 ((flag->vp_integer & 0x20) == 0)) {
497 request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
503 * Internal server statistics
505 if ((flag->vp_integer & 0x10) != 0) {
506 vp = radius_paircreate(request->reply, &request->reply->vps,
507 176, VENDORPEC_FREERADIUS);
508 if (vp) vp->vp_date = start_time.tv_sec;
509 vp = radius_paircreate(request->reply, &request->reply->vps,
510 177, VENDORPEC_FREERADIUS);
511 if (vp) vp->vp_date = hup_time.tv_sec;
513 #ifdef HAVE_PTHREAD_H
514 int i, array[RAD_LISTEN_MAX], pps[2];
516 thread_pool_queue_stats(array, pps);
518 for (i = 0; i <= 4; i++) {
519 vp = radius_paircreate(request->reply, &request->reply->vps,
520 162 + i, VENDORPEC_FREERADIUS);
523 vp->vp_integer = array[i];
526 for (i = 0; i < 2; i++) {
527 vp = radius_paircreate(request->reply, &request->reply->vps,
528 181 + i, VENDORPEC_FREERADIUS);
531 vp->vp_integer = pps[i];
537 * For a particular client.
539 if ((flag->vp_integer & 0x20) != 0) {
541 VALUE_PAIR *server_ip, *server_port = NULL;
542 RADCLIENT *client = NULL;
543 RADCLIENT_LIST *cl = NULL;
546 * See if we need to look up the client by server
549 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
551 server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
555 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
556 cl = listener_find_client_list(&ipaddr, server_port->vp_integer);
559 * Not found: don't do anything
566 vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS, TAG_ANY);
568 memset(&ipaddr, 0, sizeof(ipaddr));
570 ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
571 client = client_find(cl, &ipaddr, IPPROTO_UDP);
574 client = client_find(cl, &ipaddr, IPPROTO_TCP);
579 * Else look it up by number.
581 } else if ((vp = pairfind(request->packet->vps, 168, VENDORPEC_FREERADIUS, TAG_ANY)) != NULL) {
582 client = client_findbynumber(cl, vp->vp_integer);
587 * If found, echo it back, along with
588 * the requested statistics.
590 pairadd(&request->reply->vps, paircopyvp(request->reply, vp));
593 * When retrieving client by number, also
594 * echo back it's IP address.
596 if ((vp->da->type == PW_TYPE_INTEGER) &&
597 (client->ipaddr.af == AF_INET)) {
598 vp = radius_paircreate(request->reply,
599 &request->reply->vps,
600 167, VENDORPEC_FREERADIUS);
602 vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
605 if (client->ipaddr.prefix != 32) {
606 vp = radius_paircreate(request->reply,
607 &request->reply->vps,
608 169, VENDORPEC_FREERADIUS);
610 vp->vp_integer = client->ipaddr.prefix;
616 pairadd(&request->reply->vps,
617 paircopyvp(request->reply, server_ip));
620 pairadd(&request->reply->vps,
621 paircopyvp(request->reply, server_port));
624 if ((flag->vp_integer & 0x01) != 0) {
625 request_stats_addvp(request, client_authvp,
628 #ifdef WITH_ACCOUNTING
629 if ((flag->vp_integer & 0x01) != 0) {
630 request_stats_addvp(request, client_acctvp,
634 } /* else client wasn't found, don't echo it back */
638 * For a particular "listen" socket.
640 if (((flag->vp_integer & 0x40) != 0) &&
641 ((flag->vp_integer & 0x03) != 0)) {
643 VALUE_PAIR *server_ip, *server_port;
647 * See if we need to look up the server by socket
650 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
651 if (!server_ip) return;
653 server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
654 if (!server_port) return;
657 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
658 this = listener_find_byipaddr(&ipaddr,
659 server_port->vp_integer,
663 * Not found: don't do anything
667 pairadd(&request->reply->vps,
668 paircopyvp(request->reply, server_ip));
669 pairadd(&request->reply->vps,
670 paircopyvp(request->reply, server_port));
672 if (((flag->vp_integer & 0x01) != 0) &&
673 ((request->listener->type == RAD_LISTEN_AUTH) ||
674 (request->listener->type == RAD_LISTEN_NONE))) {
675 request_stats_addvp(request, authvp, &this->stats);
678 #ifdef WITH_ACCOUNTING
679 if (((flag->vp_integer & 0x02) != 0) &&
680 ((request->listener->type == RAD_LISTEN_ACCT) ||
681 (request->listener->type == RAD_LISTEN_NONE))) {
682 request_stats_addvp(request, acctvp, &this->stats);
691 if (((flag->vp_integer & 0x80) != 0) &&
692 ((flag->vp_integer & 0x03) != 0)) {
694 VALUE_PAIR *server_ip, *server_port;
698 * See if we need to look up the server by socket
701 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
702 if (!server_ip) return;
704 server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
705 if (!server_port) return;
708 memset(&ipaddr, 0, sizeof(ipaddr));
711 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
712 home = home_server_find(&ipaddr, server_port->vp_integer,
716 * Not found: don't do anything
720 pairadd(&request->reply->vps,
721 paircopyvp(request->reply, server_ip));
722 pairadd(&request->reply->vps,
723 paircopyvp(request->reply, server_port));
725 vp = radius_paircreate(request->reply, &request->reply->vps,
726 172, VENDORPEC_FREERADIUS);
727 if (vp) vp->vp_integer = home->currently_outstanding;
729 vp = radius_paircreate(request->reply, &request->reply->vps,
730 173, VENDORPEC_FREERADIUS);
731 if (vp) vp->vp_integer = home->state;
733 if ((home->state == HOME_STATE_ALIVE) &&
734 (home->revive_time.tv_sec != 0)) {
735 vp = radius_paircreate(request->reply, &request->reply->vps,
736 175, VENDORPEC_FREERADIUS);
737 if (vp) vp->vp_date = home->revive_time.tv_sec;
740 if ((home->state == HOME_STATE_ALIVE) &&
741 (home->ema.window > 0)) {
742 vp = radius_paircreate(request->reply,
743 &request->reply->vps,
744 178, VENDORPEC_FREERADIUS);
745 if (vp) vp->vp_integer = home->ema.window;
746 vp = radius_paircreate(request->reply,
747 &request->reply->vps,
748 179, VENDORPEC_FREERADIUS);
749 if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
750 vp = radius_paircreate(request->reply,
751 &request->reply->vps,
752 180, VENDORPEC_FREERADIUS);
753 if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
757 if (home->state == HOME_STATE_IS_DEAD) {
758 vp = radius_paircreate(request->reply, &request->reply->vps,
759 174, VENDORPEC_FREERADIUS);
760 if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
764 * Show more information...
766 * FIXME: do this for clients, too!
768 vp = radius_paircreate(request->reply, &request->reply->vps,
769 184, VENDORPEC_FREERADIUS);
770 if (vp) vp->vp_date = home->last_packet_recv;
772 vp = radius_paircreate(request->reply, &request->reply->vps,
773 185, VENDORPEC_FREERADIUS);
774 if (vp) vp->vp_date = home->last_packet_sent;
776 if (((flag->vp_integer & 0x01) != 0) &&
777 (home->type == HOME_TYPE_AUTH)) {
778 request_stats_addvp(request, proxy_authvp,
782 #ifdef WITH_ACCOUNTING
783 if (((flag->vp_integer & 0x02) != 0) &&
784 (home->type == HOME_TYPE_ACCT)) {
785 request_stats_addvp(request, proxy_acctvp,
790 #endif /* WITH_PROXY */
793 void radius_stats_init(int flag)
796 gettimeofday(&start_time, NULL);
797 hup_time = start_time; /* it's just nicer this way */
799 gettimeofday(&hup_time, NULL);
803 void radius_stats_ema(fr_stats_ema_t *ema,
804 struct timeval *start, struct timeval *end)
808 #ifdef WITH_STATS_DEBUG
811 if (ema->window == 0) return;
813 rad_assert(start->tv_sec <= end->tv_sec);
819 if (ema->window > 10000) ema->window = 10000;
821 ema->f1 = (2 * F_EMA_SCALE) / (ema->window + 1);
822 ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
826 tdiff = start->tv_sec;
827 tdiff -= end->tv_sec;
830 if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
832 micro += start->tv_usec;
833 micro -= end->tv_usec;
837 if (ema->ema1 == 0) {
843 diff = ema->f1 * (micro - ema->ema1);
844 ema->ema1 += (diff / 1000000);
846 diff = ema->f10 * (micro - ema->ema10);
847 ema->ema10 += (diff / 1000000);
851 #ifdef WITH_STATS_DEBUG
852 DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
853 n, micro / PREC, (micro / EMA_SCALE) % USEC,
854 ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
855 ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
860 #endif /* WITH_STATS */