]>
git.saurik.com Git - apple/network_cmds.git/blob - unbound/daemon/stats.c
2 * daemon/stats.c - collect runtime performance indicators.
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
6 * This software is open source.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 * This file describes the data structure used to collect runtime performance
40 * numbers. These 'statistics' may be of interest to the operator.
47 #include <sys/types.h>
48 #include "daemon/stats.h"
49 #include "daemon/worker.h"
50 #include "daemon/daemon.h"
51 #include "services/mesh.h"
52 #include "services/outside_network.h"
53 #include "util/config_file.h"
54 #include "util/tube.h"
55 #include "util/timehist.h"
56 #include "util/net_help.h"
57 #include "validator/validator.h"
58 #include "ldns/sbuffer.h"
59 #include "services/cache/rrset.h"
60 #include "services/cache/infra.h"
61 #include "validator/val_kcache.h"
63 /** add timers and the values do not overflow or become negative */
65 timeval_add(struct timeval
* d
, const struct timeval
* add
)
68 d
->tv_sec
+= add
->tv_sec
;
69 d
->tv_usec
+= add
->tv_usec
;
70 if(d
->tv_usec
> 1000000) {
71 d
->tv_usec
-= 1000000;
77 void server_stats_init(struct server_stats
* stats
, struct config_file
* cfg
)
79 memset(stats
, 0, sizeof(*stats
));
80 stats
->extended
= cfg
->stat_extended
;
83 void server_stats_querymiss(struct server_stats
* stats
, struct worker
* worker
)
85 stats
->num_queries_missed_cache
++;
86 stats
->sum_query_list_size
+= worker
->env
.mesh
->all
.count
;
87 if(worker
->env
.mesh
->all
.count
> stats
->max_query_list_size
)
88 stats
->max_query_list_size
= worker
->env
.mesh
->all
.count
;
91 void server_stats_prefetch(struct server_stats
* stats
, struct worker
* worker
)
93 stats
->num_queries_prefetch
++;
94 /* changes the query list size so account that, like a querymiss */
95 stats
->sum_query_list_size
+= worker
->env
.mesh
->all
.count
;
96 if(worker
->env
.mesh
->all
.count
> stats
->max_query_list_size
)
97 stats
->max_query_list_size
= worker
->env
.mesh
->all
.count
;
100 void server_stats_log(struct server_stats
* stats
, struct worker
* worker
,
103 log_info("server stats for thread %d: %u queries, "
104 "%u answers from cache, %u recursions, %u prefetch",
105 threadnum
, (unsigned)stats
->num_queries
,
106 (unsigned)(stats
->num_queries
-
107 stats
->num_queries_missed_cache
),
108 (unsigned)stats
->num_queries_missed_cache
,
109 (unsigned)stats
->num_queries_prefetch
);
110 log_info("server stats for thread %d: requestlist max %u avg %g "
111 "exceeded %u jostled %u", threadnum
,
112 (unsigned)stats
->max_query_list_size
,
113 (stats
->num_queries_missed_cache
+stats
->num_queries_prefetch
)?
114 (double)stats
->sum_query_list_size
/
115 (stats
->num_queries_missed_cache
+
116 stats
->num_queries_prefetch
) : 0.0,
117 (unsigned)worker
->env
.mesh
->stats_dropped
,
118 (unsigned)worker
->env
.mesh
->stats_jostled
);
121 /** get rrsets bogus number from validator */
123 get_rrset_bogus(struct worker
* worker
)
125 int m
= modstack_find(&worker
->env
.mesh
->mods
, "validator");
130 ve
= (struct val_env
*)worker
->env
.modinfo
[m
];
131 lock_basic_lock(&ve
->bogus_lock
);
132 r
= ve
->num_rrset_bogus
;
133 if(!worker
->env
.cfg
->stat_cumulative
)
134 ve
->num_rrset_bogus
= 0;
135 lock_basic_unlock(&ve
->bogus_lock
);
140 server_stats_compile(struct worker
* worker
, struct stats_info
* s
, int reset
)
144 s
->svr
= worker
->stats
;
145 s
->mesh_num_states
= worker
->env
.mesh
->all
.count
;
146 s
->mesh_num_reply_states
= worker
->env
.mesh
->num_reply_states
;
147 s
->mesh_jostled
= worker
->env
.mesh
->stats_jostled
;
148 s
->mesh_dropped
= worker
->env
.mesh
->stats_dropped
;
149 s
->mesh_replies_sent
= worker
->env
.mesh
->replies_sent
;
150 s
->mesh_replies_sum_wait
= worker
->env
.mesh
->replies_sum_wait
;
151 s
->mesh_time_median
= timehist_quartile(worker
->env
.mesh
->histogram
,
154 /* add in the values from the mesh */
155 s
->svr
.ans_secure
+= worker
->env
.mesh
->ans_secure
;
156 s
->svr
.ans_bogus
+= worker
->env
.mesh
->ans_bogus
;
157 s
->svr
.ans_rcode_nodata
+= worker
->env
.mesh
->ans_nodata
;
159 s
->svr
.ans_rcode
[i
] += worker
->env
.mesh
->ans_rcode
[i
];
160 timehist_export(worker
->env
.mesh
->histogram
, s
->svr
.hist
,
162 /* values from outside network */
163 s
->svr
.unwanted_replies
= worker
->back
->unwanted_replies
;
164 s
->svr
.qtcp_outgoing
= worker
->back
->num_tcp_outgoing
;
166 /* get and reset validator rrset bogus number */
167 s
->svr
.rrset_bogus
= get_rrset_bogus(worker
);
169 /* get cache sizes */
170 s
->svr
.msg_cache_count
= count_slabhash_entries(worker
->env
.msg_cache
);
171 s
->svr
.rrset_cache_count
= count_slabhash_entries(&worker
->env
.rrset_cache
->table
);
172 s
->svr
.infra_cache_count
= count_slabhash_entries(worker
->env
.infra_cache
->hosts
);
173 if(worker
->env
.key_cache
)
174 s
->svr
.key_cache_count
= count_slabhash_entries(worker
->env
.key_cache
->slab
);
175 else s
->svr
.key_cache_count
= 0;
177 if(reset
&& !worker
->env
.cfg
->stat_cumulative
) {
178 worker_stats_clear(worker
);
182 void server_stats_obtain(struct worker
* worker
, struct worker
* who
,
183 struct stats_info
* s
, int reset
)
185 uint8_t *reply
= NULL
;
188 /* just fill it in */
189 server_stats_compile(worker
, s
, reset
);
192 /* communicate over tube */
193 verbose(VERB_ALGO
, "write stats cmd");
195 worker_send_cmd(who
, worker_cmd_stats
);
196 else worker_send_cmd(who
, worker_cmd_stats_noreset
);
197 verbose(VERB_ALGO
, "wait for stats reply");
198 if(!tube_read_msg(worker
->cmd
, &reply
, &len
, 0))
199 fatal_exit("failed to read stats over cmd channel");
200 if(len
!= (uint32_t)sizeof(*s
))
201 fatal_exit("stats on cmd channel wrong length %d %d",
202 (int)len
, (int)sizeof(*s
));
203 memcpy(s
, reply
, (size_t)len
);
207 void server_stats_reply(struct worker
* worker
, int reset
)
210 server_stats_compile(worker
, &s
, reset
);
211 verbose(VERB_ALGO
, "write stats replymsg");
212 if(!tube_write_msg(worker
->daemon
->workers
[0]->cmd
,
213 (uint8_t*)&s
, sizeof(s
), 0))
214 fatal_exit("could not write stat values over cmd channel");
217 void server_stats_add(struct stats_info
* total
, struct stats_info
* a
)
219 total
->svr
.num_queries
+= a
->svr
.num_queries
;
220 total
->svr
.num_queries_missed_cache
+= a
->svr
.num_queries_missed_cache
;
221 total
->svr
.num_queries_prefetch
+= a
->svr
.num_queries_prefetch
;
222 total
->svr
.sum_query_list_size
+= a
->svr
.sum_query_list_size
;
223 /* the max size reached is upped to higher of both */
224 if(a
->svr
.max_query_list_size
> total
->svr
.max_query_list_size
)
225 total
->svr
.max_query_list_size
= a
->svr
.max_query_list_size
;
227 if(a
->svr
.extended
) {
229 total
->svr
.qtype_big
+= a
->svr
.qtype_big
;
230 total
->svr
.qclass_big
+= a
->svr
.qclass_big
;
231 total
->svr
.qtcp
+= a
->svr
.qtcp
;
232 total
->svr
.qtcp_outgoing
+= a
->svr
.qtcp_outgoing
;
233 total
->svr
.qipv6
+= a
->svr
.qipv6
;
234 total
->svr
.qbit_QR
+= a
->svr
.qbit_QR
;
235 total
->svr
.qbit_AA
+= a
->svr
.qbit_AA
;
236 total
->svr
.qbit_TC
+= a
->svr
.qbit_TC
;
237 total
->svr
.qbit_RD
+= a
->svr
.qbit_RD
;
238 total
->svr
.qbit_RA
+= a
->svr
.qbit_RA
;
239 total
->svr
.qbit_Z
+= a
->svr
.qbit_Z
;
240 total
->svr
.qbit_AD
+= a
->svr
.qbit_AD
;
241 total
->svr
.qbit_CD
+= a
->svr
.qbit_CD
;
242 total
->svr
.qEDNS
+= a
->svr
.qEDNS
;
243 total
->svr
.qEDNS_DO
+= a
->svr
.qEDNS_DO
;
244 total
->svr
.ans_rcode_nodata
+= a
->svr
.ans_rcode_nodata
;
245 total
->svr
.ans_secure
+= a
->svr
.ans_secure
;
246 total
->svr
.ans_bogus
+= a
->svr
.ans_bogus
;
247 total
->svr
.rrset_bogus
+= a
->svr
.rrset_bogus
;
248 total
->svr
.unwanted_replies
+= a
->svr
.unwanted_replies
;
249 total
->svr
.unwanted_queries
+= a
->svr
.unwanted_queries
;
250 for(i
=0; i
<STATS_QTYPE_NUM
; i
++)
251 total
->svr
.qtype
[i
] += a
->svr
.qtype
[i
];
252 for(i
=0; i
<STATS_QCLASS_NUM
; i
++)
253 total
->svr
.qclass
[i
] += a
->svr
.qclass
[i
];
254 for(i
=0; i
<STATS_OPCODE_NUM
; i
++)
255 total
->svr
.qopcode
[i
] += a
->svr
.qopcode
[i
];
256 for(i
=0; i
<STATS_RCODE_NUM
; i
++)
257 total
->svr
.ans_rcode
[i
] += a
->svr
.ans_rcode
[i
];
258 for(i
=0; i
<NUM_BUCKETS_HIST
; i
++)
259 total
->svr
.hist
[i
] += a
->svr
.hist
[i
];
262 total
->mesh_num_states
+= a
->mesh_num_states
;
263 total
->mesh_num_reply_states
+= a
->mesh_num_reply_states
;
264 total
->mesh_jostled
+= a
->mesh_jostled
;
265 total
->mesh_dropped
+= a
->mesh_dropped
;
266 total
->mesh_replies_sent
+= a
->mesh_replies_sent
;
267 timeval_add(&total
->mesh_replies_sum_wait
, &a
->mesh_replies_sum_wait
);
268 /* the medians are averaged together, this is not as accurate as
269 * taking the median over all of the data, but is good and fast
270 * added up here, division later*/
271 total
->mesh_time_median
+= a
->mesh_time_median
;
274 void server_stats_insquery(struct server_stats
* stats
, struct comm_point
* c
,
275 uint16_t qtype
, uint16_t qclass
, struct edns_data
* edns
,
276 struct comm_reply
* repinfo
)
278 uint16_t flags
= sldns_buffer_read_u16_at(c
->buffer
, 2);
279 if(qtype
< STATS_QTYPE_NUM
)
280 stats
->qtype
[qtype
]++;
281 else stats
->qtype_big
++;
282 if(qclass
< STATS_QCLASS_NUM
)
283 stats
->qclass
[qclass
]++;
284 else stats
->qclass_big
++;
285 stats
->qopcode
[ LDNS_OPCODE_WIRE(sldns_buffer_begin(c
->buffer
)) ]++;
286 if(c
->type
!= comm_udp
)
288 if(repinfo
&& addr_is_ip6(&repinfo
->addr
, repinfo
->addrlen
))
306 if(edns
->edns_present
) {
308 if( (edns
->bits
& EDNS_DO
) )
313 void server_stats_insrcode(struct server_stats
* stats
, sldns_buffer
* buf
)
315 if(stats
->extended
&& sldns_buffer_limit(buf
) != 0) {
316 int r
= (int)LDNS_RCODE_WIRE( sldns_buffer_begin(buf
) );
317 stats
->ans_rcode
[r
] ++;
318 if(r
== 0 && LDNS_ANCOUNT( sldns_buffer_begin(buf
) ) == 0)
319 stats
->ans_rcode_nodata
++;