]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/Metrics.m
mDNSResponder-1310.80.1.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Metrics.m
1 /*
2 * Copyright (c) 2016-2019 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #import "Metrics.h"
18
19 #if MDNSRESPONDER_SUPPORTS(APPLE, METRICS)
20 #import <CoreUtils/SoftLinking.h>
21 #import <WirelessDiagnostics/AWDDNSDomainStats.h>
22 #import <WirelessDiagnostics/AWDMDNSResponderDNSMessageSizeStats.h>
23 #import <WirelessDiagnostics/AWDMDNSResponderDNSStatistics.h>
24 #import <WirelessDiagnostics/AWDMDNSResponderServicesStats.h>
25 #import <WirelessDiagnostics/AWDMetricIds_MDNSResponder.h>
26 #import <WirelessDiagnostics/WirelessDiagnostics.h>
27
28 #import "DNSCommon.h"
29 #import "mDNSMacOSX.h"
30 #import "DebugServices.h"
31
32 //===========================================================================================================================
33 // External Frameworks
34 //===========================================================================================================================
35
36 SOFT_LINK_FRAMEWORK(PrivateFrameworks, WirelessDiagnostics)
37
38 // AWDServerConnection class
39
40 SOFT_LINK_CLASS(WirelessDiagnostics, AWDServerConnection)
41
42 #define AWDServerConnectionSoft getAWDServerConnectionClass()
43
44 // Classes for query stats
45
46 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSStatistics)
47 SOFT_LINK_CLASS(WirelessDiagnostics, AWDDNSDomainStats)
48
49 #define AWDMDNSResponderDNSStatisticsSoft getAWDMDNSResponderDNSStatisticsClass()
50 #define AWDDNSDomainStatsSoft getAWDDNSDomainStatsClass()
51
52 // Classes for services stats
53
54 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMetricManager)
55
56 #define AWDMetricManagerSoft getAWDMetricManagerClass()
57
58 // Classes for DNS message size stats
59
60 SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSMessageSizeStats)
61
62 #define AWDMDNSResponderDNSMessageSizeStatsSoft getAWDMDNSResponderDNSMessageSizeStatsClass()
63
64 //===========================================================================================================================
65 // Macros
66 //===========================================================================================================================
67
68 #define countof(X) (sizeof(X) / sizeof(X[0]))
69 #define countof_field(TYPE, FIELD) countof(((TYPE *)0)->FIELD)
70 #define ForgetMem(X) do {if(*(X)) {free(*(X)); *(X) = NULL;}} while(0)
71
72 //===========================================================================================================================
73 // Constants
74 //===========================================================================================================================
75
76 #define kQueryStatsMaxQuerySendCount 10
77 #define kQueryStatsSendCountBinCount (kQueryStatsMaxQuerySendCount + 1)
78 #define kQueryStatsLatencyBinCount 55
79 #define kQueryStatsExpiredAnswerStateCount (ExpiredAnswer_EnumCount)
80 #define kQueryStatsDNSOverTCPStateCount (DNSOverTCP_EnumCount)
81
82 //===========================================================================================================================
83 // Data structures
84 //===========================================================================================================================
85
86 // Data structures for query stats.
87
88 typedef struct QueryStats QueryStats;
89 typedef struct DNSHistSet DNSHistSet;
90 typedef mDNSBool (*QueryNameTest_f)(const QueryStats *inStats, const domainname *inQueryName);
91
92 struct QueryStats
93 {
94 QueryStats * next; // Pointer to next domain stats in list.
95 const char * domainStr; // Domain (see below) as a C string.
96 uint8_t * domain; // Domain for which these stats are collected.
97 const char * altDomainStr; // Alt domain string to use in the AWD version of the stats instead of domainStr.
98 DNSHistSet * nonCellular; // Query stats for queries sent over non-cellular interfaces.
99 DNSHistSet * cellular; // Query stats for queries sent over cellular interfaces.
100 QueryNameTest_f test; // Function that tests whether a given query's stats belong based on the query name.
101 int labelCount; // Number of labels in domain name. Used for domain name comparisons.
102 mDNSBool terminal; // If true and test passes, then no other QueryStats on the list should be visited.
103 };
104
105 check_compile_time(sizeof(QueryStats) <= 64);
106
107 // DNSHist contains the per domain per network type histogram data that goes in a DNSDomainStats protobuf message. See
108 // <rdar://problem/23980546> MDNSResponder.proto update.
109 //
110 // answeredQuerySendCountBins
111 //
112 // An array of 11 histogram bins. The value at index i, for 0 <= i <= 9, is the number of times that an answered DNS query
113 // was sent i times. The value at index 10 is the number of times that an answered query was sent 10+ times.
114 //
115 // unansweredQuerySendCountBins
116 //
117 // An array of 11 histogram bins. The value at index i, for 0 <= i <= 9, is the number of times that an unanswered DNS query
118 // was sent i times. The value at index 10 is the number of times that an unanswered query was sent 10+ times.
119 //
120 // responseLatencyBins
121 //
122 // An array of 55 histogram bins. Each array value is the number of DNS queries that were answered in a paricular time
123 // interval. The 55 consecutive non-overlapping time intervals have the following non-inclusive upper bounds (all values are
124 // in milliseconds): 1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
125 // 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1500, 2000, 2500, 3000, 3500, 4000,
126 // 4500, 5000, 6000, 7000, 8000, 9000, 10000, ∞.
127
128 typedef struct
129 {
130 uint16_t unansweredQuerySendCountBins[kQueryStatsSendCountBinCount];
131 uint16_t unansweredQueryDurationBins[kQueryStatsLatencyBinCount];
132 uint16_t answeredQuerySendCountBins[kQueryStatsSendCountBinCount];
133 uint16_t responseLatencyBins[kQueryStatsLatencyBinCount];
134 uint16_t negAnsweredQuerySendCountBins[kQueryStatsSendCountBinCount];
135 uint16_t negResponseLatencyBins[kQueryStatsLatencyBinCount];
136 uint32_t expiredAnswerStateBins[kQueryStatsExpiredAnswerStateCount];
137 uint32_t dnsOverTCPStateBins[kQueryStatsDNSOverTCPStateCount];
138
139 } DNSHist;
140
141 check_compile_time(sizeof(DNSHist) <= 512);
142 check_compile_time(countof_field(DNSHist, unansweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
143 check_compile_time(countof_field(DNSHist, answeredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
144 check_compile_time(countof_field(DNSHist, negAnsweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1));
145 check_compile_time(countof_field(DNSHist, expiredAnswerStateBins) == (kQueryStatsExpiredAnswerStateCount));
146 check_compile_time(countof_field(DNSHist, dnsOverTCPStateBins) == (kQueryStatsDNSOverTCPStateCount));
147
148 // Important: Do not modify kResponseLatencyMsLimits because the code used to generate AWD reports expects the response
149 // latency histogram bins to observe these time interval upper bounds.
150
151 static const mDNSu32 kResponseLatencyMsLimits[] =
152 {
153 1, 2, 3, 4, 5,
154 10, 20, 30, 40, 50, 60, 70, 80, 90,
155 100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
156 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950,
157 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500,
158 5000, 6000, 7000, 8000, 9000,
159 10000
160 };
161
162 check_compile_time(countof(kResponseLatencyMsLimits) == 54);
163 check_compile_time(countof_field(DNSHist, unansweredQueryDurationBins) == (countof(kResponseLatencyMsLimits) + 1));
164 check_compile_time(countof_field(DNSHist, responseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1));
165 check_compile_time(countof_field(DNSHist, negResponseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1));
166
167 struct DNSHistSet
168 {
169 DNSHist * histA; // Histogram data for queries for A resource records.
170 DNSHist * histAAAA; // Histogram data for queries for AAAA resource records.
171 };
172
173 typedef struct
174 {
175 const char * domainStr;
176 const char * altDomainStr;
177 QueryNameTest_f test;
178 mDNSBool terminal;
179
180 } QueryStatsArgs;
181
182 // Data structures for DNS message size stats.
183
184 #define kQuerySizeBinWidth 16
185 #define kQuerySizeBinMax 512
186 #define kQuerySizeBinCount ((kQuerySizeBinMax / kQuerySizeBinWidth) + 1)
187
188 check_compile_time(kQuerySizeBinWidth > 0);
189 check_compile_time(kQuerySizeBinCount > 0);
190 check_compile_time((kQuerySizeBinMax % kQuerySizeBinWidth) == 0);
191
192 #define kResponseSizeBinWidth 16
193 #define kResponseSizeBinMax 512
194 #define kResponseSizeBinCount ((kResponseSizeBinMax / kResponseSizeBinWidth) + 1)
195
196 check_compile_time(kResponseSizeBinWidth > 0);
197 check_compile_time(kResponseSizeBinCount > 0);
198 check_compile_time((kResponseSizeBinMax % kResponseSizeBinWidth) == 0);
199
200 typedef struct
201 {
202 uint32_t querySizeBins[kQuerySizeBinCount];
203 uint32_t responseSizeBins[kResponseSizeBinCount];
204
205 } DNSMessageSizeStats;
206
207 check_compile_time(sizeof(DNSMessageSizeStats) <= 264);
208
209 //===========================================================================================================================
210 // Local Prototypes
211 //===========================================================================================================================
212
213 // Query stats
214
215 mDNSlocal mStatus QueryStatsCreate(const char *inDomainStr, const char *inAltDomainStr, QueryNameTest_f inTest, mDNSBool inTerminal, QueryStats **outStats);
216 mDNSlocal void QueryStatsFree(QueryStats *inStats);
217 mDNSlocal void QueryStatsFreeList(QueryStats *inList);
218 mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, ExpiredAnswerMetric inExpiredAnswerState, DNSOverTCPMetric inDNSOverTCPState, mDNSu32 inLatencyMs, mDNSBool inForCell);
219 mDNSlocal const char * QueryStatsGetDomainString(const QueryStats *inStats);
220 mDNSlocal mDNSBool QueryStatsDomainTest(const QueryStats *inStats, const domainname *inQueryName);
221 mDNSlocal mDNSBool QueryStatsHostnameTest(const QueryStats *inStats, const domainname *inQueryName);
222 mDNSlocal mDNSBool QueryStatsContentiCloudTest(const QueryStats *inStats, const domainname *inQueryName);
223 mDNSlocal mDNSBool QueryStatsCourierPushTest(const QueryStats *inStats, const domainname *inQueryName);
224
225 // DNS message size stats
226
227 mDNSlocal mStatus DNSMessageSizeStatsCreate(DNSMessageSizeStats **outStats);
228 mDNSlocal void DNSMessageSizeStatsFree(DNSMessageSizeStats *inStats);
229
230 mDNSlocal mStatus CreateQueryStatsList(QueryStats **outList);
231 mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID);
232 mDNSlocal mStatus SubmitAWDMetricQueryStats(void);
233 mDNSlocal mStatus SubmitAWDMetricDNSMessageSizeStats(void);
234 mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats);
235 mDNSlocal void LogDNSHistSetToFD(int fd, const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell);
236 mDNSlocal void LogDNSHistToFD(int fd, const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType);
237 mDNSlocal void LogDNSHistSendCountsToFD(int fd, const uint16_t inSendCountBins[kQueryStatsSendCountBinCount]);
238 mDNSlocal void LogDNSHistLatenciesToFD(int fd, const uint16_t inLatencyBins[kQueryStatsLatencyBinCount]);
239 mDNSlocal void LogDNSMessageSizeStatsToFD(int fd, const uint32_t *inBins, size_t inBinCount, unsigned int inBinWidth);
240
241 //===========================================================================================================================
242 // Histogram Bin Helpers
243 //===========================================================================================================================
244
245 #define INCREMENT_BIN_DEFINITION(BIN_SIZE) \
246 mDNSlocal void IncrementBin ## BIN_SIZE (uint ## BIN_SIZE ## _t *inBin) \
247 { \
248 if (*inBin < UINT ## BIN_SIZE ## _MAX) ++(*inBin); \
249 } \
250 extern int _MetricsDummyVariable
251
252 INCREMENT_BIN_DEFINITION(16);
253 INCREMENT_BIN_DEFINITION(32);
254
255 // Note: The return value is the size (in number of elements) of the smallest contiguous sub-array that contains the first
256 // bin and all bins with non-zero values.
257
258 #define COPY_BINS_DEFINITION(BIN_SIZE) \
259 mDNSlocal size_t CopyBins ## BIN_SIZE (uint32_t *inDstBins, uint ## BIN_SIZE ## _t *inSrcBins, size_t inBinCount) \
260 { \
261 if (inBinCount == 0) return (0); \
262 size_t minCount = 1; \
263 for (size_t i = 0; i < inBinCount; ++i) \
264 { \
265 inDstBins[i] = inSrcBins[i]; \
266 if (inDstBins[i] > 0) minCount = i + 1; \
267 } \
268 return (minCount); \
269 } \
270 extern int _MetricsDummyVariable
271
272 COPY_BINS_DEFINITION(16);
273 COPY_BINS_DEFINITION(32);
274
275 //===========================================================================================================================
276 // Globals
277 //===========================================================================================================================
278
279 static AWDServerConnection * gAWDServerConnection = nil;
280 static QueryStats * gQueryStatsList = NULL;
281 static DNSMessageSizeStats * gDNSMessageSizeStats = NULL;
282
283 // Important: Do not add to this list without getting privacy approval. See <rdar://problem/24155761&26397203&34763471>.
284
285 static const QueryStatsArgs kQueryStatsArgs[] =
286 {
287 { ".", NULL, QueryStatsDomainTest, mDNSfalse },
288 { "", "alt:*-courier.push.apple.com.", QueryStatsCourierPushTest, mDNSfalse },
289 { "apple.com.", NULL, QueryStatsDomainTest, mDNStrue },
290 { "gateway.icloud.com.", "alt:gateway.icloud.com", QueryStatsHostnameTest, mDNSfalse },
291 { "", "alt:*-content.icloud.com.", QueryStatsContentiCloudTest, mDNSfalse },
292 { "icloud.com.", NULL, QueryStatsDomainTest, mDNStrue },
293 { "mzstatic.com.", NULL, QueryStatsDomainTest, mDNStrue },
294 { "google.com.", NULL, QueryStatsDomainTest, mDNStrue },
295 { "baidu.com.", NULL, QueryStatsDomainTest, mDNStrue },
296 { "yahoo.com.", NULL, QueryStatsDomainTest, mDNStrue },
297 { "qq.com.", NULL, QueryStatsDomainTest, mDNStrue }
298 };
299
300 check_compile_time(countof(kQueryStatsArgs) == 11);
301
302 //===========================================================================================================================
303 // MetricsInit
304 //===========================================================================================================================
305
306 mStatus MetricsInit(void)
307 {
308 @autoreleasepool
309 {
310 gAWDServerConnection = [[AWDServerConnectionSoft alloc]
311 initWithComponentId: AWDComponentId_MDNSResponder
312 andBlockOnConfiguration: NO];
313
314 if (gAWDServerConnection)
315 {
316 [gAWDServerConnection
317 registerQueriableMetricCallback: ^(UInt32 inMetricID)
318 {
319 SubmitAWDMetric(inMetricID);
320 }
321 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_DNSStatistics];
322
323 [gAWDServerConnection
324 registerQueriableMetricCallback: ^(UInt32 inMetricID)
325 {
326 SubmitAWDMetric(inMetricID);
327 }
328 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_ServicesStats];
329
330 [gAWDServerConnection
331 registerQueriableMetricCallback: ^(UInt32 inMetricID)
332 {
333 SubmitAWDMetric(inMetricID);
334 }
335 forIdentifier: (UInt32)AWDMetricId_MDNSResponder_DNSMessageSizeStats];
336 }
337 else
338 {
339 LogMsg("MetricsInit: failed to create AWD server connection.");
340 }
341 }
342
343 if( gAWDServerConnection )
344 {
345 CreateQueryStatsList(&gQueryStatsList);
346 DNSMessageSizeStatsCreate(&gDNSMessageSizeStats);
347 }
348
349 return (mStatus_NoError);
350 }
351
352 //===========================================================================================================================
353 // MetricsUpdateDNSQueryStats
354 //===========================================================================================================================
355
356 mDNSexport void MetricsUpdateDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, ExpiredAnswerMetric inExpiredAnswerState, DNSOverTCPMetric inDNSOverTCPState, mDNSu32 inLatencyMs, mDNSBool inForCell)
357 {
358 QueryStats * stats;
359 mDNSBool match;
360
361 require_quiet(gAWDServerConnection, exit);
362 require_quiet((inType == kDNSType_A) || (inType == kDNSType_AAAA), exit);
363
364 for (stats = gQueryStatsList; stats; stats = stats->next)
365 {
366 match = stats->test(stats, inQueryName);
367 if (match)
368 {
369 QueryStatsUpdate(stats, inType, inRR, inSendCount, inExpiredAnswerState, inDNSOverTCPState, inLatencyMs, inForCell);
370 if (stats->terminal) break;
371 }
372 }
373
374 exit:
375 return;
376 }
377
378 //===========================================================================================================================
379 // MetricsUpdateDNSQuerySize
380 //===========================================================================================================================
381
382 mDNSlocal void UpdateMessageSizeCounts(uint32_t *inBins, size_t inBinCount, unsigned int inBinWidth, uint32_t inSize);
383
384 mDNSexport void MetricsUpdateDNSQuerySize(mDNSu32 inSize)
385 {
386 if (!gDNSMessageSizeStats) return;
387 UpdateMessageSizeCounts(gDNSMessageSizeStats->querySizeBins, kQuerySizeBinCount, kQuerySizeBinWidth, inSize);
388 }
389
390 mDNSlocal void UpdateMessageSizeCounts(uint32_t *inBins, size_t inBinCount, unsigned int inBinWidth, uint32_t inSize)
391 {
392 size_t i;
393
394 if (inSize == 0) return;
395 i = (inSize - 1) / inBinWidth;
396 if (i >= inBinCount) i = inBinCount - 1;
397 IncrementBin32(&inBins[i]);
398 }
399
400 //===========================================================================================================================
401 // MetricsUpdateDNSResponseSize
402 //===========================================================================================================================
403
404 mDNSexport void MetricsUpdateDNSResponseSize(mDNSu32 inSize)
405 {
406 if (!gDNSMessageSizeStats) return;
407 UpdateMessageSizeCounts(gDNSMessageSizeStats->responseSizeBins, kResponseSizeBinCount, kResponseSizeBinWidth, inSize);
408 }
409
410 //===========================================================================================================================
411 // LogMetrics
412 //===========================================================================================================================
413
414 mDNSexport void LogMetricsToFD(int fd)
415 {
416 QueryStats * stats;
417
418 LogToFD(fd, "gAWDServerConnection %p", gAWDServerConnection);
419 LogToFD(fd, "---- DNS query stats by domain -----");
420
421 for (stats = gQueryStatsList; stats; stats = stats->next)
422 {
423 if (!stats->nonCellular && !stats->cellular)
424 {
425 LogToFD(fd, "No data for %s", QueryStatsGetDomainString(stats));
426 continue;
427 }
428 if (stats->nonCellular) LogDNSHistSetToFD(fd, stats->nonCellular, QueryStatsGetDomainString(stats), mDNSfalse);
429 if (stats->cellular) LogDNSHistSetToFD(fd, stats->cellular, QueryStatsGetDomainString(stats), mDNStrue);
430 }
431
432 LogToFD(fd, "---- Num of Services Registered -----");
433 LogToFD(fd, "Current_number_of_services_registered :[%d], Max_number_of_services_registered :[%d]",
434 curr_num_regservices, max_num_regservices);
435
436 if (gDNSMessageSizeStats)
437 {
438 LogToFD(fd, "---- DNS query size stats ---");
439 LogDNSMessageSizeStatsToFD(fd, gDNSMessageSizeStats->querySizeBins, kQuerySizeBinCount, kQuerySizeBinWidth);
440
441 LogToFD(fd, "-- DNS response size stats --");
442 LogDNSMessageSizeStatsToFD(fd, gDNSMessageSizeStats->responseSizeBins, kResponseSizeBinCount, kResponseSizeBinWidth);
443 }
444 else
445 {
446 LogToFD(fd, "No DNS message size stats.");
447 }
448 }
449
450 //===========================================================================================================================
451 // QueryStatsCreate
452 //===========================================================================================================================
453
454 mDNSlocal mStatus StringToDomainName(const char *inString, uint8_t **outDomainName);
455
456 mDNSlocal mStatus QueryStatsCreate(const char *inDomainStr, const char *inAltDomainStr, QueryNameTest_f inTest, mDNSBool inTerminal, QueryStats **outStats)
457 {
458 mStatus err;
459 QueryStats * obj;
460
461 obj = (QueryStats *)calloc(1, sizeof(*obj));
462 require_action_quiet(obj, exit, err = mStatus_NoMemoryErr);
463
464 obj->domainStr = inDomainStr;
465 err = StringToDomainName(obj->domainStr, &obj->domain);
466 require_noerr_quiet(err, exit);
467
468 obj->altDomainStr = inAltDomainStr;
469 obj->test = inTest;
470 obj->labelCount = CountLabels((const domainname *)obj->domain);
471 obj->terminal = inTerminal;
472
473 *outStats = obj;
474 obj = NULL;
475 err = mStatus_NoError;
476
477 exit:
478 if (obj) QueryStatsFree(obj);
479 return (err);
480 }
481
482 mDNSlocal mStatus StringToDomainName(const char *inString, uint8_t **outDomainName)
483 {
484 mStatus err;
485 uint8_t * domainPtr = NULL;
486 size_t domainLen;
487 const mDNSu8 * ptr;
488 domainname domain;
489
490 if (strcmp(inString, ".") == 0)
491 {
492 domain.c[0] = 0;
493 }
494 else
495 {
496 ptr = MakeDomainNameFromDNSNameString(&domain, inString);
497 require_action_quiet(ptr, exit, err = mStatus_BadParamErr);
498 }
499 domainLen = DomainNameLength(&domain);
500
501 domainPtr = (uint8_t *)malloc(domainLen);
502 require_action_quiet(domainPtr, exit, err = mStatus_NoMemoryErr);
503
504 memcpy(domainPtr, domain.c, domainLen);
505
506 *outDomainName = domainPtr;
507 domainPtr = NULL;
508 err = mStatus_NoError;
509
510 exit:
511 return(err);
512 }
513
514 //===========================================================================================================================
515 // QueryStatsFree
516 //===========================================================================================================================
517
518 mDNSlocal void QueryStatsFree(QueryStats *inStats)
519 {
520 ForgetMem(&inStats->domain);
521 if (inStats->nonCellular)
522 {
523 ForgetMem(&inStats->nonCellular->histA);
524 ForgetMem(&inStats->nonCellular->histAAAA);
525 free(inStats->nonCellular);
526 inStats->nonCellular = NULL;
527 }
528 if (inStats->cellular)
529 {
530 ForgetMem(&inStats->cellular->histA);
531 ForgetMem(&inStats->cellular->histAAAA);
532 free(inStats->cellular);
533 inStats->cellular = NULL;
534 }
535 free(inStats);
536 }
537
538 //===========================================================================================================================
539 // QueryStatsFreeList
540 //===========================================================================================================================
541
542 mDNSlocal void QueryStatsFreeList(QueryStats *inList)
543 {
544 QueryStats * stats;
545
546 while ((stats = inList) != NULL)
547 {
548 inList = stats->next;
549 QueryStatsFree(stats);
550 }
551 }
552
553 //===========================================================================================================================
554 // QueryStatsUpdate
555 //===========================================================================================================================
556
557 mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, ExpiredAnswerMetric inExpiredAnswerState, DNSOverTCPMetric inDNSOverTCPState, mDNSu32 inLatencyMs, mDNSBool inForCell)
558 {
559 mStatus err;
560 DNSHistSet * set;
561 DNSHistSet ** pSet;
562 DNSHist * hist;
563 DNSHist ** pHist;
564 int i;
565
566 require_action_quiet(inRR || (inQuerySendCount > 0), exit, err = mStatus_NoError);
567 require_action_quiet((inType == kDNSType_A) || (inType == kDNSType_AAAA), exit, err = mStatus_NoError);
568
569 pSet = inForCell ? &inStats->cellular : &inStats->nonCellular;
570 if ((set = *pSet) == NULL)
571 {
572 set = (DNSHistSet *)calloc(1, sizeof(*set));
573 require_action_quiet(set, exit, err = mStatus_NoMemoryErr);
574 *pSet = set;
575 }
576 pHist = (inType == kDNSType_A) ? &set->histA : &set->histAAAA;
577 if ((hist = *pHist) == NULL)
578 {
579 hist = (DNSHist *)calloc(1, sizeof(*hist));
580 require_action_quiet(hist, exit, err = mStatus_NoMemoryErr);
581 *pHist = hist;
582 }
583
584 if (inRR)
585 {
586 uint16_t * sendCountBins;
587 uint16_t * latencyBins;
588 const mDNSBool isNegative = (inRR->RecordType == kDNSRecordTypePacketNegative);
589
590 i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount);
591
592 sendCountBins = isNegative ? hist->negAnsweredQuerySendCountBins : hist->answeredQuerySendCountBins;
593 IncrementBin16(&sendCountBins[i]);
594
595 if (inQuerySendCount > 0)
596 {
597 for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {}
598 latencyBins = isNegative ? hist->negResponseLatencyBins : hist->responseLatencyBins;
599 IncrementBin16(&latencyBins[i]);
600 }
601 }
602 else
603 {
604 i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount);
605 IncrementBin16(&hist->unansweredQuerySendCountBins[i]);
606
607 for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {}
608 IncrementBin16(&hist->unansweredQueryDurationBins[i]);
609 }
610 IncrementBin32(&hist->expiredAnswerStateBins[Min(inExpiredAnswerState, (kQueryStatsExpiredAnswerStateCount - 1))]);
611 IncrementBin32(&hist->dnsOverTCPStateBins[Min(inDNSOverTCPState, (kQueryStatsDNSOverTCPStateCount - 1))]);
612 err = mStatus_NoError;
613
614 exit:
615 return (err);
616 }
617
618 //===========================================================================================================================
619 // QueryStatsGetDomainString
620 //===========================================================================================================================
621
622 mDNSlocal const char * QueryStatsGetDomainString(const QueryStats *inStats)
623 {
624 return (inStats->altDomainStr ? inStats->altDomainStr : inStats->domainStr);
625 }
626
627 //===========================================================================================================================
628 // QueryStatsDomainTest
629 //===========================================================================================================================
630
631 mDNSlocal mDNSBool QueryStatsDomainTest(const QueryStats *inStats, const domainname *inQueryName)
632 {
633 const domainname * parentDomain;
634 int labelCount;
635
636 if (inStats->domain[0] == 0) return (mDNStrue);
637
638 labelCount = CountLabels(inQueryName);
639 if (labelCount < inStats->labelCount) return (mDNSfalse);
640
641 parentDomain = SkipLeadingLabels(inQueryName, labelCount - inStats->labelCount);
642 return (SameDomainName(parentDomain, (const domainname *)inStats->domain));
643 }
644
645 //===========================================================================================================================
646 // QueryStatsHostnameTest
647 //===========================================================================================================================
648
649 mDNSlocal mDNSBool QueryStatsHostnameTest(const QueryStats *inStats, const domainname *inQueryName)
650 {
651 return (SameDomainName(inQueryName, (const domainname *)inStats->domain));
652 }
653
654 //===========================================================================================================================
655 // QueryStatsContentiCloudTest
656 //===========================================================================================================================
657
658 mDNSlocal const uint8_t *LocateLabelSuffix(const uint8_t *inLabel, const uint8_t *inSuffixPtr, size_t inSuffixLen);
659
660 #define kContentSuffixStr "-content"
661
662 mDNSlocal mDNSBool QueryStatsContentiCloudTest(const QueryStats *inStats, const domainname *inQueryName)
663 {
664 const mDNSu8 * const firstLabel = inQueryName->c;
665 const uint8_t * suffix;
666 const domainname * parentDomain;
667 int labelCount;
668
669 (void) inStats; // Unused.
670
671 labelCount = CountLabels(inQueryName);
672 if (labelCount != 3) return (mDNSfalse);
673
674 suffix = LocateLabelSuffix(firstLabel, (const uint8_t *)kContentSuffixStr, sizeof_string(kContentSuffixStr));
675 if (suffix && (suffix > &firstLabel[1]))
676 {
677 parentDomain = SkipLeadingLabels(inQueryName, 1);
678 if (SameDomainName(parentDomain, (const domainname *)"\x6" "icloud" "\x3" "com"))
679 {
680 return (mDNStrue);
681 }
682 }
683
684 return (mDNSfalse);
685 }
686
687 mDNSlocal const uint8_t *LocateLabelSuffix(const uint8_t *inLabel, const uint8_t *inSuffixPtr, size_t inSuffixLen)
688 {
689 const uint8_t * ptr;
690 const uint8_t * lp;
691 const uint8_t * sp;
692 size_t len;
693 const size_t labelLen = inLabel[0];
694
695 if (labelLen < inSuffixLen) return (NULL);
696
697 ptr = &inLabel[1 + labelLen - inSuffixLen];
698 lp = ptr;
699 sp = inSuffixPtr;
700 for (len = inSuffixLen; len > 0; --len)
701 {
702 if (tolower(*lp) != tolower(*sp)) return (NULL);
703 ++lp;
704 ++sp;
705 }
706
707 return (ptr);
708 }
709
710 //===========================================================================================================================
711 // QueryStatsCourierPushTest
712 //===========================================================================================================================
713
714 #define kCourierSuffixStr "-courier"
715
716 mDNSlocal mDNSBool QueryStatsCourierPushTest(const QueryStats *inStats, const domainname *inQueryName)
717 {
718 const mDNSu8 * const firstLabel = inQueryName->c;
719 const uint8_t * suffix;
720 const uint8_t * ptr;
721 const domainname * parentDomain;
722 int labelCount;
723
724 (void) inStats; // Unused.
725
726 labelCount = CountLabels(inQueryName);
727 if (labelCount != 4) return (mDNSfalse);
728
729 suffix = LocateLabelSuffix(firstLabel, (const mDNSu8 *)kCourierSuffixStr, sizeof_string(kCourierSuffixStr));
730 if (suffix && (suffix > &firstLabel[1]))
731 {
732 for (ptr = &firstLabel[1]; ptr < suffix; ++ptr)
733 {
734 if (!isdigit(*ptr)) break;
735 }
736 if (ptr == suffix)
737 {
738 parentDomain = SkipLeadingLabels(inQueryName, 1);
739 if (SameDomainName(parentDomain, (const domainname *)"\x4" "push" "\x5" "apple" "\x3" "com"))
740 {
741 return (mDNStrue);
742 }
743 }
744 }
745
746 return (mDNSfalse);
747 }
748
749 //===========================================================================================================================
750 // DNSMessageSizeStatsCreate
751 //===========================================================================================================================
752
753 mDNSlocal mStatus DNSMessageSizeStatsCreate(DNSMessageSizeStats **outStats)
754 {
755 mStatus err;
756 DNSMessageSizeStats * stats;
757
758 stats = (DNSMessageSizeStats *)calloc(1, sizeof(*stats));
759 require_action_quiet(stats, exit, err = mStatus_NoMemoryErr);
760
761 *outStats = stats;
762 err = mStatus_NoError;
763
764 exit:
765 return (err);
766 }
767
768 //===========================================================================================================================
769 // DNSMessageSizeStatsFree
770 //===========================================================================================================================
771
772 mDNSlocal void DNSMessageSizeStatsFree(DNSMessageSizeStats *inStats)
773 {
774 free(inStats);
775 }
776
777 //===========================================================================================================================
778 // CreateQueryStatsList
779 //===========================================================================================================================
780
781 mDNSlocal mStatus CreateQueryStatsList(QueryStats **outList)
782 {
783 mStatus err;
784 QueryStats ** p;
785 QueryStats * stats;
786 const QueryStatsArgs * args;
787 const QueryStatsArgs * const end = kQueryStatsArgs + countof(kQueryStatsArgs);
788 QueryStats * list = NULL;
789
790 p = &list;
791 for (args = kQueryStatsArgs; args < end; ++args)
792 {
793 err = QueryStatsCreate(args->domainStr, args->altDomainStr, args->test, args->terminal, &stats);
794 require_noerr_quiet(err, exit);
795
796 *p = stats;
797 p = &stats->next;
798 }
799
800 *outList = list;
801 list = NULL;
802 err = mStatus_NoError;
803
804 exit:
805 QueryStatsFreeList(list);
806 return (err);
807 }
808
809 //===========================================================================================================================
810 // SubmitAWDMetric
811 //===========================================================================================================================
812
813 mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID)
814 {
815 mStatus err;
816
817 switch (inMetricID)
818 {
819 case AWDMetricId_MDNSResponder_DNSStatistics:
820 err = SubmitAWDMetricQueryStats();
821 break;
822
823 case AWDMetricId_MDNSResponder_ServicesStats:
824 [AWDMetricManagerSoft postMetricWithId:AWDMetricId_MDNSResponder_ServicesStats unsignedIntegerValue:max_num_regservices];
825 KQueueLock();
826 // reset the no of max services since we want to collect the max no of services registered per AWD submission period
827 max_num_regservices = curr_num_regservices;
828 KQueueUnlock("SubmitAWDSimpleMetricServiceStats");
829 err = mStatus_NoError;
830 break;
831
832 case AWDMetricId_MDNSResponder_DNSMessageSizeStats:
833 err = SubmitAWDMetricDNSMessageSizeStats();
834 break;
835
836 default:
837 err = mStatus_UnsupportedErr;
838 break;
839 }
840
841 if (err) LogMsg("SubmitAWDMetric for metric ID 0x%08X failed with error %d", inMetricID, err);
842 return (err);
843 }
844
845 //===========================================================================================================================
846 // SubmitAWDMetricQueryStats
847 //===========================================================================================================================
848
849 mDNSlocal mStatus AddQueryStats(AWDMDNSResponderDNSStatistics *inMetric, const QueryStats *inStats);
850 mDNSlocal mStatus AddDNSHistSet(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell);
851
852 mDNSlocal mStatus SubmitAWDMetricQueryStats(void)
853 {
854 mStatus err;
855 BOOL success;
856 QueryStats * stats;
857 QueryStats * statsList;
858 QueryStats * newStatsList;
859 AWDMetricContainer * container = nil;
860 AWDMDNSResponderDNSStatistics * metric = nil;
861
862 newStatsList = NULL;
863 CreateQueryStatsList(&newStatsList);
864
865 KQueueLock();
866 statsList = gQueryStatsList;
867 gQueryStatsList = newStatsList;
868 KQueueUnlock("SubmitAWDMetricQueryStats");
869
870 container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSStatistics];
871 require_action_quiet(container, exit, err = mStatus_UnknownErr);
872
873 metric = [[AWDMDNSResponderDNSStatisticsSoft alloc] init];
874 require_action_quiet(metric, exit, err = mStatus_UnknownErr);
875
876 while ((stats = statsList) != NULL)
877 {
878 err = AddQueryStats(metric, stats);
879 require_noerr_quiet(err, exit);
880
881 statsList = stats->next;
882 QueryStatsFree(stats);
883 }
884
885 container.metric = metric;
886 success = [gAWDServerConnection submitMetric:container];
887 LogMsg("SubmitAWDMetricQueryStats: metric submission %s.", success ? "succeeded" : "failed");
888 err = success ? mStatus_NoError : mStatus_UnknownErr;
889
890 exit:
891 QueryStatsFreeList(statsList);
892 return (err);
893 }
894
895 mDNSlocal mStatus AddQueryStats(AWDMDNSResponderDNSStatistics *inMetric, const QueryStats *inStats)
896 {
897 mStatus err;
898
899 if (inStats->nonCellular)
900 {
901 err = AddDNSHistSet(inMetric, inStats->nonCellular, QueryStatsGetDomainString(inStats), mDNSfalse);
902 require_noerr_quiet(err, exit);
903 }
904 if (inStats->cellular)
905 {
906 err = AddDNSHistSet(inMetric, inStats->cellular, QueryStatsGetDomainString(inStats), mDNStrue);
907 require_noerr_quiet(err, exit);
908 }
909 err = mStatus_NoError;
910
911 exit:
912 return (err);
913 }
914
915 mDNSlocal mStatus AddDNSHistSet(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell)
916 {
917 mStatus err;
918 AWDDNSDomainStats * awdStats;
919
920 if (inSet->histA)
921 {
922 err = CreateAWDDNSDomainStats(inSet->histA, inDomain, inForCell, AWDDNSDomainStats_RecordType_A, &awdStats);
923 require_noerr_quiet(err, exit);
924
925 [inMetric addStats:awdStats];
926 }
927 if (inSet->histAAAA)
928 {
929 err = CreateAWDDNSDomainStats(inSet->histAAAA, inDomain, inForCell, AWDDNSDomainStats_RecordType_AAAA, &awdStats);
930 require_noerr_quiet(err, exit);
931
932 [inMetric addStats:awdStats];
933 }
934 err = mStatus_NoError;
935
936 exit:
937 return (err);
938 }
939
940 //===========================================================================================================================
941 // SubmitAWDMetricDNSMessageSizeStats
942 //===========================================================================================================================
943
944 mDNSlocal mStatus SubmitAWDMetricDNSMessageSizeStats(void)
945 {
946 mStatus err;
947 DNSMessageSizeStats * stats;
948 DNSMessageSizeStats * newStats;
949 AWDMetricContainer * container;
950 AWDMDNSResponderDNSMessageSizeStats * metric = nil;
951 BOOL success;
952
953 newStats = NULL;
954 DNSMessageSizeStatsCreate(&newStats);
955
956 KQueueLock();
957 stats = gDNSMessageSizeStats;
958 gDNSMessageSizeStats = newStats;
959 KQueueUnlock("SubmitAWDMetricDNSMessageSizeStats");
960
961 container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSMessageSizeStats];
962 require_action_quiet(container, exit, err = mStatus_UnknownErr);
963
964 metric = [[AWDMDNSResponderDNSMessageSizeStatsSoft alloc] init];
965 require_action_quiet(metric, exit, err = mStatus_UnknownErr);
966
967 if (stats)
968 {
969 size_t binCount;
970 uint32_t bins[Max(kQuerySizeBinCount, kResponseSizeBinCount)];
971
972 // Set query size counts.
973
974 binCount = CopyBins32(bins, stats->querySizeBins, kQuerySizeBinCount);
975 [metric setQuerySizeCounts:bins count:(NSUInteger)binCount];
976
977 // Set response size counts.
978
979 binCount = CopyBins32(bins, stats->responseSizeBins, kResponseSizeBinCount);
980 [metric setResponseSizeCounts:bins count:(NSUInteger)binCount];
981 }
982
983 container.metric = metric;
984 success = [gAWDServerConnection submitMetric:container];
985 LogMsg("SubmitAWDMetricDNSMessageSizeStats: metric submission %s.", success ? "succeeded" : "failed");
986 err = success ? mStatus_NoError : mStatus_UnknownErr;
987
988 exit:
989 if (stats) DNSMessageSizeStatsFree(stats);
990 return (err);
991 }
992
993 //===========================================================================================================================
994 // CreateAWDDNSDomainStats
995 //===========================================================================================================================
996
997 mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats)
998 {
999 mStatus err;
1000 AWDDNSDomainStats * awdStats = nil;
1001 NSString * domain = nil;
1002 size_t binCount;
1003 uint32_t sendCountBins[kQueryStatsSendCountBinCount];
1004 uint32_t latencyBins[kQueryStatsLatencyBinCount];
1005 uint32_t expiredAnswerBins[kQueryStatsExpiredAnswerStateCount];
1006 uint32_t dnsOverTCPBins[kQueryStatsDNSOverTCPStateCount];
1007
1008 awdStats = [[AWDDNSDomainStatsSoft alloc] init];
1009 require_action_quiet(awdStats, exit, err = mStatus_UnknownErr);
1010
1011 domain = [[NSString alloc] initWithUTF8String:inDomain];
1012 require_action_quiet(domain, exit, err = mStatus_UnknownErr);
1013
1014 awdStats.domain = domain;
1015 awdStats.networkType = inForCell ? AWDDNSDomainStats_NetworkType_Cellular : AWDDNSDomainStats_NetworkType_NonCellular;
1016 awdStats.recordType = inType;
1017
1018 // Positively answered query send counts
1019
1020 binCount = CopyBins16(sendCountBins, inHist->answeredQuerySendCountBins, kQueryStatsSendCountBinCount);
1021 [awdStats setAnsweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
1022
1023 // binCount > 1 means that at least one of the non-zero send count bins had a non-zero count, i.e., at least one query
1024 // was sent out on the wire. In that case, include the associated latency bins as well.
1025
1026 if (binCount > 1)
1027 {
1028 binCount = CopyBins16(latencyBins, inHist->responseLatencyBins, kQueryStatsLatencyBinCount);
1029 [awdStats setResponseLatencyMs:latencyBins count:(NSUInteger)binCount];
1030 }
1031
1032 // Negatively answered query send counts
1033
1034 binCount = CopyBins16(sendCountBins, inHist->negAnsweredQuerySendCountBins, kQueryStatsSendCountBinCount);
1035 [awdStats setNegAnsweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
1036
1037 if (binCount > 1)
1038 {
1039 binCount = CopyBins16(latencyBins, inHist->negResponseLatencyBins, kQueryStatsLatencyBinCount);
1040 [awdStats setNegResponseLatencyMs:latencyBins count:(NSUInteger)binCount];
1041 }
1042
1043 // Unanswered query send counts
1044
1045 binCount = CopyBins16(sendCountBins, inHist->unansweredQuerySendCountBins, kQueryStatsSendCountBinCount);
1046 [awdStats setUnansweredQuerySendCounts:sendCountBins count:(NSUInteger)binCount];
1047
1048 if (binCount > 1)
1049 {
1050 binCount = CopyBins16(latencyBins, inHist->unansweredQueryDurationBins, kQueryStatsLatencyBinCount);
1051 [awdStats setUnansweredQueryDurationMs:latencyBins count:(NSUInteger)binCount];
1052 }
1053
1054 // Expired answers states
1055
1056 binCount = CopyBins32(expiredAnswerBins, inHist->expiredAnswerStateBins, kQueryStatsExpiredAnswerStateCount);
1057 [awdStats setExpiredAnswerStates:expiredAnswerBins count:(NSUInteger)binCount];
1058
1059 // DNS Over TCP states
1060
1061 binCount = CopyBins32(dnsOverTCPBins, inHist->dnsOverTCPStateBins, kQueryStatsDNSOverTCPStateCount);
1062 [awdStats setDnsOverTCPStates:dnsOverTCPBins count:(NSUInteger)binCount];
1063
1064 *outStats = awdStats;
1065 err = mStatus_NoError;
1066
1067 exit:
1068 return (err);
1069 }
1070
1071 //===========================================================================================================================
1072 // LogDNSHistSetToFD
1073 //===========================================================================================================================
1074
1075 mDNSlocal void LogDNSHistSetToFD(int fd, const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell)
1076 {
1077 if (inSet->histA) LogDNSHistToFD(fd, inSet->histA, inDomain, inForCell, "A");
1078 if (inSet->histAAAA) LogDNSHistToFD(fd, inSet->histAAAA, inDomain, inForCell, "AAAA");
1079 }
1080
1081 //===========================================================================================================================
1082 // LogDNSHistToFD
1083 //===========================================================================================================================
1084
1085 #define Percent(N, D) (((N) * 100) / (D)), ((((N) * 10000) / (D)) % 100)
1086 #define PercentFmt "%3u.%02u"
1087 #define LogStatToFD(FILE_DESCRIPTOR, LABEL, COUNT, ACCUMULATOR, TOTAL) \
1088 LogToFD((FILE_DESCRIPTOR), "%s %5u " PercentFmt " " PercentFmt, (LABEL), (COUNT), Percent(COUNT, TOTAL), Percent(ACCUMULATOR, TOTAL))
1089
1090 mDNSlocal void LogDNSHistToFD(int fd, const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType)
1091 {
1092 unsigned int totalAnswered;
1093 unsigned int totalNegAnswered;
1094 unsigned int totalUnanswered;
1095 int i;
1096
1097 totalAnswered = 0;
1098 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1099 {
1100 totalAnswered += inHist->answeredQuerySendCountBins[i];
1101 }
1102
1103 totalNegAnswered = 0;
1104 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1105 {
1106 totalNegAnswered += inHist->negAnsweredQuerySendCountBins[i];
1107 }
1108
1109 totalUnanswered = 0;
1110 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1111 {
1112 totalUnanswered += inHist->unansweredQuerySendCountBins[i];
1113 }
1114
1115 LogToFD(fd, "Domain: %s (%s, %s)", inDomain, inForCell ? "C" : "NC", inType);
1116 LogToFD(fd, "Answered questions %10u", totalAnswered);
1117 LogToFD(fd, "Negatively answered questions %10u", totalNegAnswered);
1118 LogToFD(fd, "Unanswered questions %10u", totalUnanswered);
1119 LogToFD(fd, "Expired - no cached answer %10u", inHist->expiredAnswerStateBins[ExpiredAnswer_Allowed]);
1120 LogToFD(fd, "Expired - answered from cache %10u", inHist->expiredAnswerStateBins[ExpiredAnswer_AnsweredWithCache]);
1121 LogToFD(fd, "Expired - answered expired %10u", inHist->expiredAnswerStateBins[ExpiredAnswer_AnsweredWithExpired]);
1122 LogToFD(fd, "Expired - cache changed %10u", inHist->expiredAnswerStateBins[ExpiredAnswer_ExpiredAnswerChanged]);
1123 LogToFD(fd, "DNSoTCP - truncated %10u", inHist->dnsOverTCPStateBins[DNSOverTCP_Truncated]);
1124 LogToFD(fd, "DNSoTCP - suspicious %10u", inHist->dnsOverTCPStateBins[DNSOverTCP_Suspicious]);
1125 LogToFD(fd, "DNSoTCP - suspicious defense %10u", inHist->dnsOverTCPStateBins[DNSOverTCP_SuspiciousDefense]);
1126 LogToFD(fd, "-- Query send counts ---------");
1127 LogDNSHistSendCountsToFD(fd, inHist->answeredQuerySendCountBins);
1128 LogToFD(fd, "-- Query send counts (NAQs) --");
1129 LogDNSHistSendCountsToFD(fd, inHist->negAnsweredQuerySendCountBins);
1130
1131 if (totalAnswered > inHist->answeredQuerySendCountBins[0])
1132 {
1133 LogToFD(fd, "--- Response times -----------");
1134 LogDNSHistLatenciesToFD(fd, inHist->responseLatencyBins);
1135 }
1136
1137 if (totalNegAnswered > inHist->negAnsweredQuerySendCountBins[0])
1138 {
1139 LogToFD(fd, "--- Response times (NAQs) ----");
1140 LogDNSHistLatenciesToFD(fd, inHist->negResponseLatencyBins);
1141 }
1142
1143 if (totalUnanswered > 0)
1144 {
1145 LogToFD(fd, "--- Unanswered query times ---");
1146 LogDNSHistLatenciesToFD(fd, inHist->unansweredQueryDurationBins);
1147 }
1148 }
1149
1150 //===========================================================================================================================
1151 // LogDNSHistSendCountsToFD
1152 //===========================================================================================================================
1153
1154 mDNSlocal void LogDNSHistSendCountsToFD(int fd, const uint16_t inSendCountBins[kQueryStatsSendCountBinCount])
1155 {
1156 uint32_t total;
1157 char label[16];
1158 int i;
1159
1160 total = 0;
1161 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1162 {
1163 total += inSendCountBins[i];
1164 }
1165
1166 if (total > 0)
1167 {
1168 uint32_t accumulator = 0;
1169
1170 for (i = 0; i < kQueryStatsSendCountBinCount; ++i)
1171 {
1172 accumulator += inSendCountBins[i];
1173 if (i < (kQueryStatsSendCountBinCount - 1))
1174 {
1175 snprintf(label, sizeof(label), "%2d ", i);
1176 }
1177 else
1178 {
1179 snprintf(label, sizeof(label), "%2d+", i);
1180 }
1181 LogStatToFD(fd, label, inSendCountBins[i], accumulator, total);
1182 if (accumulator == total) break;
1183 }
1184 }
1185 else
1186 {
1187 LogToFD(fd, "No data.");
1188 }
1189 }
1190
1191 //===========================================================================================================================
1192 // LogDNSHistLatenciesToFD
1193 //===========================================================================================================================
1194
1195 mDNSlocal void LogDNSHistLatenciesToFD(int fd,
1196 const uint16_t inLatencyBins[kQueryStatsLatencyBinCount])
1197 {
1198 uint32_t total;
1199 int i;
1200 char label[16];
1201
1202 total = 0;
1203 for (i = 0; i < kQueryStatsLatencyBinCount; ++i)
1204 {
1205 total += inLatencyBins[i];
1206 }
1207
1208 if (total > 0)
1209 {
1210 uint32_t accumulator = 0;
1211
1212 for (i = 0; i < kQueryStatsLatencyBinCount; ++i)
1213 {
1214 accumulator += inLatencyBins[i];
1215 if (i < (int)countof(kResponseLatencyMsLimits))
1216 {
1217 snprintf(label, sizeof(label), "< %5u ms", kResponseLatencyMsLimits[i]);
1218 }
1219 else
1220 {
1221 snprintf(label, sizeof(label), "< ∞ ms");
1222 }
1223 LogStatToFD(fd, label, inLatencyBins[i], accumulator, total);
1224 if (accumulator == total) break;
1225 }
1226 }
1227 else
1228 {
1229 LogToFD(fd, "No data.");
1230 }
1231 }
1232
1233 //===========================================================================================================================
1234 // LogDNSMessageSizeStatsToFD
1235 //===========================================================================================================================
1236
1237 mDNSlocal void LogDNSMessageSizeStatsToFD(int fd, const uint32_t *inBins, size_t inBinCount, unsigned int inBinWidth)
1238 {
1239 size_t i;
1240 uint32_t total;
1241
1242 total = 0;
1243 for (i = 0; i < inBinCount; ++i)
1244 {
1245 total += inBins[i];
1246 }
1247
1248 if (total > 0)
1249 {
1250 uint32_t accumulator;
1251 unsigned int lower, upper;
1252 char label[16];
1253
1254 accumulator = 0;
1255 upper = 0;
1256 for (i = 0; i < inBinCount; ++i)
1257 {
1258 accumulator += inBins[i];
1259 lower = upper + 1;
1260 if (i < (inBinCount - 1))
1261 {
1262 upper += inBinWidth;
1263 snprintf(label, sizeof(label), "%3u - %-3u", lower, upper);
1264 }
1265 else
1266 {
1267 snprintf(label, sizeof(label), "%3u+ ", lower);
1268 }
1269 LogStatToFD(fd, label, inBins[i], accumulator, total);
1270 if (accumulator == total) break;
1271 }
1272 }
1273 else
1274 {
1275 LogToFD(fd, "No data.");
1276 }
1277 }
1278
1279 #endif // MDNSRESPONDER_SUPPORTS(APPLE, METRICS)