]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
da327a1b | 22 | #define __FAVOR_BSD |
870a0fe4 AT |
23 | #ifdef HAVE_CONFIG_H |
24 | #include "config.h" | |
25 | #endif | |
75709ae8 | 26 | #if HAVE_BOOST_GE_148 |
ef890b79 | 27 | #include "histog.hh" |
75709ae8 | 28 | #endif |
ef890b79 | 29 | |
a640a9d4 BH |
30 | #include "statbag.hh" |
31 | #include "dnspcap.hh" | |
32 | #include "dnsparser.hh" | |
68ca579c | 33 | #include "dnsname.hh" |
a48e03da | 34 | #include <boost/format.hpp> |
a640a9d4 BH |
35 | #include <map> |
36 | #include <set> | |
37 | #include <fstream> | |
da327a1b | 38 | #include <algorithm> |
9a450843 | 39 | #include "anadns.hh" |
611a846e | 40 | #include <boost/program_options.hpp> |
ef890b79 | 41 | #include <unordered_set> |
611a846e | 42 | #include <boost/logic/tribool.hpp> |
ce9309b7 | 43 | #include "arguments.hh" |
10f4eea8 | 44 | #include "namespaces.hh" |
168eb01c | 45 | #include "dnsrecords.hh" |
6264512b | 46 | #include "statnode.hh" |
a640a9d4 | 47 | |
611a846e | 48 | namespace po = boost::program_options; |
49 | po::variables_map g_vm; | |
a640a9d4 | 50 | |
ce9309b7 | 51 | ArgvMap& arg() |
bb85386d | 52 | { |
ce9309b7 | 53 | static ArgvMap theArg; |
54 | return theArg; | |
55 | } | |
611a846e | 56 | StatBag S; |
a640a9d4 | 57 | |
9ad511a7 | 58 | |
59 | ||
a640a9d4 BH |
60 | struct QuestionData |
61 | { | |
62 | QuestionData() : d_qcount(0), d_answercount(0) | |
63 | { | |
64 | d_firstquestiontime.tv_sec=0; | |
65 | } | |
66 | ||
67 | int d_qcount; | |
68 | int d_answercount; | |
69 | ||
f79a7a88 | 70 | struct pdns_timeval d_firstquestiontime; |
a640a9d4 BH |
71 | }; |
72 | ||
9a450843 | 73 | typedef map<QuestionIdentifier, QuestionData> statmap_t; |
a640a9d4 BH |
74 | statmap_t statmap; |
75 | ||
050e6877 | 76 | static unsigned int liveQuestions() |
ad79b1b5 | 77 | { |
78 | unsigned int ret=0; | |
ef7cd021 | 79 | for(statmap_t::value_type& val : statmap) { |
ad79b1b5 | 80 | if(!val.second.d_answercount) |
81 | ret++; | |
82 | // if(val.second.d_qcount > val.second.d_answercount) | |
83 | // ret+= val.second.d_qcount - val.second.d_answercount; | |
84 | } | |
85 | return ret; | |
86 | } | |
87 | ||
88 | struct LiveCounts | |
89 | { | |
90 | unsigned int questions; | |
91 | unsigned int answers; | |
92 | unsigned int outstanding; | |
93 | ||
94 | LiveCounts() | |
95 | { | |
96 | questions=answers=outstanding=0; | |
97 | } | |
98 | ||
99 | LiveCounts operator-(const LiveCounts& rhs) | |
100 | { | |
101 | LiveCounts ret; | |
102 | ret.questions = questions - rhs.questions; | |
103 | ret.answers = answers - rhs.answers; | |
104 | ret.outstanding = outstanding; | |
105 | return ret; | |
106 | } | |
107 | }; | |
108 | ||
d73de874 | 109 | static void visitor(const StatNode* node, const StatNode::Stat& /* selfstat */, const StatNode::Stat& childstat) |
6264512b | 110 | { |
111 | // 20% servfails, >100 children, on average less than 2 copies of a query | |
112 | // >100 different subqueries | |
113 | double dups=1.0*childstat.queries/node->children.size(); | |
114 | if(dups > 2.0) | |
115 | return; | |
116 | if(1.0*childstat.servfails / childstat.queries > 0.2 && node->children.size()>100) { | |
117 | cout<<node->fullname<<", servfails: "<<childstat.servfails<<", nxdomains: "<<childstat.nxdomains<<", remotes: "<<childstat.remotes.size()<<", children: "<<node->children.size()<<", childstat.queries: "<<childstat.queries; | |
118 | cout<<", dups2: "<<dups<<endl; | |
119 | for(const StatNode::Stat::remotes_t::value_type& rem : childstat.remotes) { | |
120 | cout<<"source: "<<node->fullname<<"\t"<<rem.first.toString()<<"\t"<<rem.second<<endl; | |
121 | } | |
122 | } | |
123 | } | |
ce9309b7 | 124 | |
050e6877 | 125 | static const struct timeval operator-(const struct pdns_timeval& lhs, const struct pdns_timeval& rhs) |
ef890b79 | 126 | { |
54c38ce0 | 127 | struct timeval a{lhs.tv_sec, static_cast<suseconds_t>(lhs.tv_usec)}, b{rhs.tv_sec, static_cast<suseconds_t>(rhs.tv_usec)}; |
ef890b79 | 128 | return operator-(a,b); |
129 | } | |
130 | ||
131 | ||
a640a9d4 BH |
132 | int main(int argc, char** argv) |
133 | try | |
134 | { | |
611a846e | 135 | po::options_description desc("Allowed options"), hidden, alloptions; |
136 | desc.add_options() | |
137 | ("help,h", "produce help message") | |
0670347d | 138 | ("version", "print version number") |
611a846e | 139 | ("rd", po::value<bool>(), "If set to true, only process RD packets, to false only non-RD, unset: both") |
140 | ("ipv4", po::value<bool>()->default_value(true), "Process IPv4 packets") | |
141 | ("ipv6", po::value<bool>()->default_value(true), "Process IPv6 packets") | |
75709ae8 | 142 | #if HAVE_BOOST_GE_148 |
ef890b79 | 143 | ("log-histogram", "Write a log-histogram to file 'log-histogram'") |
144 | ("full-histogram", po::value<double>(), "Write a log-histogram to file 'full-histogram' with this millisecond bin size") | |
75709ae8 | 145 | #endif |
7211d47b | 146 | ("filter-name,f", po::value<string>(), "Do statistics only for queries within this domain") |
9a07e342 | 147 | ("load-stats,l", po::value<string>()->default_value(""), "if set, emit per-second load statistics (questions, answers, outstanding)") |
ef890b79 | 148 | ("no-servfail-stats", "Don't include servfails in response time stats") |
149 | ("servfail-tree", "Figure out subtrees that generate servfails") | |
150 | ("stats-dir", po::value<string>()->default_value("."), "Directory where statistics will be saved") | |
be427910 | 151 | ("write-failures,w", po::value<string>()->default_value(""), "if set, write weird packets to this PCAP file") |
611a846e | 152 | ("verbose,v", "be verbose"); |
bb85386d | 153 | |
611a846e | 154 | hidden.add_options() |
155 | ("files", po::value<vector<string> >(), "files"); | |
156 | ||
bb85386d | 157 | alloptions.add(desc).add(hidden); |
611a846e | 158 | |
159 | po::positional_options_description p; | |
160 | p.add("files", -1); | |
161 | ||
162 | po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm); | |
163 | po::notify(g_vm); | |
bb85386d | 164 | |
be427910 | 165 | vector<string> files; |
bb85386d FM |
166 | if(g_vm.count("files")) |
167 | files = g_vm["files"].as<vector<string> >(); | |
0670347d PL |
168 | |
169 | if(g_vm.count("version")) { | |
170 | cerr<<"dnsscope "<<VERSION<<endl; | |
171 | exit(0); | |
172 | } | |
173 | ||
82b4a662 | 174 | if(files.empty() || g_vm.count("help")) { |
87aa8d9f | 175 | cerr<<"Syntax: dnsscope filename.pcap [filename2.pcap...]"<<endl; |
611a846e | 176 | cout << desc << endl; |
0670347d | 177 | exit(0); |
1cf46d65 | 178 | } |
a640a9d4 | 179 | |
7211d47b | 180 | DNSName filtername; |
181 | if(g_vm.count("filter-name")) | |
182 | filtername = DNSName(g_vm["filter-name"].as<string>()); | |
183 | uint32_t nameMismatch = 0; | |
184 | ||
9ad511a7 | 185 | StatNode root; |
186 | ||
be427910 | 187 | bool verbose = g_vm.count("verbose"); |
188 | ||
611a846e | 189 | bool haveRDFilter=0, rdFilter=0; |
190 | if(g_vm.count("rd")) { | |
191 | rdFilter = g_vm["rd"].as<bool>(); | |
192 | haveRDFilter=1; | |
ad79b1b5 | 193 | cout<<"Filtering on recursion desired="<<rdFilter<<endl; |
611a846e | 194 | } |
ad79b1b5 | 195 | else |
196 | cout<<"Warning, looking at both RD and non-RD traffic!"<<endl; | |
611a846e | 197 | |
198 | bool doIPv4 = g_vm["ipv4"].as<bool>(); | |
199 | bool doIPv6 = g_vm["ipv6"].as<bool>(); | |
9ad511a7 | 200 | bool doServFailTree = g_vm.count("servfail-tree"); |
ef890b79 | 201 | bool noservfailstats = g_vm.count("no-servfail-stats"); |
202 | int dnserrors=0, parsefail=0; | |
a640a9d4 BH |
203 | typedef map<uint32_t,uint32_t> cumul_t; |
204 | cumul_t cumul; | |
e075bae3 | 205 | unsigned int untracked=0, errorresult=0, nonRDQueries=0, queries=0; |
d2d8cafd | 206 | unsigned int ipv4DNSPackets=0, ipv6DNSPackets=0, fragmented=0, rdNonRAAnswers=0; |
207 | unsigned int answers=0, nonDNSIP=0, rdFilterMismatch=0; | |
168eb01c | 208 | unsigned int dnssecOK=0, edns=0; |
209 | unsigned int dnssecCD=0, dnssecAD=0; | |
ef890b79 | 210 | unsigned int reuses=0; |
a640a9d4 BH |
211 | typedef map<uint16_t,uint32_t> rcodes_t; |
212 | rcodes_t rcodes; | |
bb85386d | 213 | |
54007885 | 214 | time_t lowestTime=0, highestTime=0; |
ad79b1b5 | 215 | time_t lastsec=0; |
216 | LiveCounts lastcounts; | |
ef890b79 | 217 | std::unordered_set<ComboAddress, ComboAddress::addressOnlyHash> requestors, recipients, rdnonra; |
ad79b1b5 | 218 | typedef vector<pair<time_t, LiveCounts> > pcounts_t; |
219 | pcounts_t pcounts; | |
168eb01c | 220 | OPTRecordContent::report(); |
ef890b79 | 221 | |
82b4a662 | 222 | for(unsigned int fno=0; fno < files.size(); ++fno) { |
223 | PcapPacketReader pr(files[fno]); | |
c2826d2e | 224 | std::unique_ptr<PcapPacketWriter> pw=nullptr; |
82b4a662 | 225 | if(!g_vm["write-failures"].as<string>().empty()) |
2bbc9eb0 RP |
226 | pw = std::make_unique<PcapPacketWriter>(g_vm["write-failures"].as<string>(), pr); |
227 | ||
168eb01c | 228 | EDNSOpts edo; |
82b4a662 | 229 | while(pr.getUDPPacket()) { |
230 | ||
231 | if((ntohs(pr.d_udp->uh_dport)==5300 || ntohs(pr.d_udp->uh_sport)==5300 || | |
232 | ntohs(pr.d_udp->uh_dport)==53 || ntohs(pr.d_udp->uh_sport)==53) && | |
233 | pr.d_len > 12) { | |
234 | try { | |
235 | if((pr.d_ip->ip_v == 4 && !doIPv4) || (pr.d_ip->ip_v == 6 && !doIPv6)) | |
236 | continue; | |
237 | if(pr.d_ip->ip_v == 4) { | |
238 | uint16_t frag = ntohs(pr.d_ip->ip_off); | |
239 | if((frag & IP_MF) || (frag & IP_OFFMASK)) { // more fragments or IS a fragment | |
240 | fragmented++; | |
241 | continue; | |
242 | } | |
243 | } | |
ef890b79 | 244 | uint16_t qtype; |
245 | DNSName qname((const char*)pr.d_payload, pr.d_len, 12, false, &qtype); | |
246 | struct dnsheader header; | |
247 | memcpy(&header, (struct dnsheader*)pr.d_payload, 12); | |
248 | ||
249 | if(haveRDFilter && header.rd != rdFilter) { | |
82b4a662 | 250 | rdFilterMismatch++; |
be427910 | 251 | continue; |
252 | } | |
bb85386d | 253 | |
7211d47b | 254 | if(!filtername.empty() && !qname.isPartOf(filtername)) { |
255 | nameMismatch++; | |
256 | continue; | |
257 | } | |
bb85386d | 258 | |
ef890b79 | 259 | if(!header.qr) { |
260 | uint16_t udpsize, z; | |
261 | if(getEDNSUDPPayloadSizeAndZ((const char*)pr.d_payload, pr.d_len, &udpsize, &z)) { | |
262 | edns++; | |
263 | if(z & EDNSOpts::DNSSECOK) | |
264 | dnssecOK++; | |
265 | if(header.cd) | |
266 | dnssecCD++; | |
267 | if(header.ad) | |
268 | dnssecAD++; | |
269 | } | |
270 | } | |
168eb01c | 271 | |
bb85386d | 272 | if(pr.d_ip->ip_v == 4) |
82b4a662 | 273 | ++ipv4DNSPackets; |
274 | else | |
275 | ++ipv6DNSPackets; | |
bb85386d | 276 | |
82b4a662 | 277 | if(pr.d_pheader.ts.tv_sec != lastsec) { |
278 | LiveCounts lc; | |
279 | if(lastsec) { | |
280 | lc.questions = queries; | |
281 | lc.answers = answers; | |
bb85386d | 282 | lc.outstanding = liveQuestions(); |
82b4a662 | 283 | |
284 | LiveCounts diff = lc - lastcounts; | |
e32a8d46 RP |
285 | pcounts.emplace_back(pr.d_pheader.ts.tv_sec, diff); |
286 | } | |
287 | lastsec = pr.d_pheader.ts.tv_sec; | |
288 | lastcounts = lc; | |
289 | } | |
ad79b1b5 | 290 | |
54007885 PD |
291 | if(lowestTime) { lowestTime = min((time_t)lowestTime, (time_t)pr.d_pheader.ts.tv_sec); } |
292 | else { lowestTime = pr.d_pheader.ts.tv_sec; } | |
82b4a662 | 293 | highestTime=max((time_t)highestTime, (time_t)pr.d_pheader.ts.tv_sec); |
4957a608 | 294 | |
ef890b79 | 295 | QuestionIdentifier qi=QuestionIdentifier::create(pr.getSource(), pr.getDest(), header, qname, qtype); |
4957a608 | 296 | |
ef890b79 | 297 | if(!header.qr) { // question |
298 | // cout<<"Query "<<qi<<endl; | |
299 | if(!header.rd) | |
82b4a662 | 300 | nonRDQueries++; |
301 | queries++; | |
fa7c37fe | 302 | |
82b4a662 | 303 | ComboAddress rem = pr.getSource(); |
304 | rem.sin4.sin_port=0; | |
bb85386d | 305 | requestors.insert(rem); |
ce9309b7 | 306 | |
ef890b79 | 307 | QuestionData& qd=statmap[qi]; |
bb85386d | 308 | |
82b4a662 | 309 | if(!qd.d_firstquestiontime.tv_sec) |
310 | qd.d_firstquestiontime=pr.d_pheader.ts; | |
ef890b79 | 311 | else { |
312 | auto delta=makeFloat(pr.d_pheader.ts - qd.d_firstquestiontime); | |
313 | // cout<<"Reuse of "<<qi<<", delta t="<<delta<<", count="<<qd.d_qcount<<endl; | |
314 | if(delta > 2.0) { | |
315 | // cout<<"Resetting old entry for "<<qi<<", too old"<<endl; | |
316 | qd.d_qcount=0; | |
317 | qd.d_answercount=0; | |
318 | qd.d_firstquestiontime=pr.d_pheader.ts; | |
319 | } | |
320 | } | |
321 | if(qd.d_qcount++) | |
322 | reuses++; | |
ce9309b7 | 323 | } |
82b4a662 | 324 | else { // answer |
ef890b79 | 325 | // cout<<"Response "<<qi<<endl; |
326 | rcodes[header.rcode]++; | |
82b4a662 | 327 | answers++; |
ef890b79 | 328 | if(header.rd && !header.ra) { |
82b4a662 | 329 | rdNonRAAnswers++; |
330 | rdnonra.insert(pr.getDest()); | |
331 | } | |
bb85386d | 332 | |
ef890b79 | 333 | if(header.ra) { |
82b4a662 | 334 | ComboAddress rem = pr.getDest(); |
335 | rem.sin4.sin_port=0; | |
bb85386d | 336 | recipients.insert(rem); |
82b4a662 | 337 | } |
338 | ||
339 | QuestionData& qd=statmap[qi]; | |
ef890b79 | 340 | if(!qd.d_qcount) { |
341 | // cout<<"Untracked answer: "<<qi<<endl; | |
82b4a662 | 342 | untracked++; |
ef890b79 | 343 | } |
82b4a662 | 344 | |
345 | qd.d_answercount++; | |
346 | ||
347 | if(qd.d_qcount) { | |
bb85386d | 348 | uint32_t usecs= (pr.d_pheader.ts.tv_sec - qd.d_firstquestiontime.tv_sec) * 1000000 + |
82b4a662 | 349 | (pr.d_pheader.ts.tv_usec - qd.d_firstquestiontime.tv_usec) ; |
ef890b79 | 350 | |
351 | // cout<<"Usecs for "<<qi<<": "<<usecs<<endl; | |
352 | if(!noservfailstats || header.rcode != 2) | |
353 | cumul[usecs]++; | |
bb85386d FM |
354 | |
355 | if(header.rcode != 0 && header.rcode!=3) | |
82b4a662 | 356 | errorresult++; |
357 | ComboAddress rem = pr.getDest(); | |
358 | rem.sin4.sin_port=0; | |
9ad511a7 | 359 | |
82b4a662 | 360 | if(doServFailTree) |
6ecfaee2 | 361 | root.submit(qname, header.rcode, pr.d_len, false, rem); |
82b4a662 | 362 | } |
4957a608 | 363 | |
ef890b79 | 364 | if(!qd.d_qcount || qd.d_qcount == qd.d_answercount) { |
365 | // cout<<"Clearing state for "<<qi<<endl<<endl; | |
82b4a662 | 366 | statmap.erase(qi); |
ef890b79 | 367 | } |
eace2c24 RG |
368 | else { |
369 | // cout<<"State for qi remains open, qcount="<<qd.d_qcount<<", answercount="<<qd.d_answercount<<endl; | |
370 | } | |
82b4a662 | 371 | } |
82b4a662 | 372 | } |
373 | catch(std::exception& e) { | |
374 | if(verbose) | |
375 | cout<<"error parsing packet: "<<e.what()<<endl; | |
376 | ||
377 | if(pw) | |
378 | pw->write(); | |
ef890b79 | 379 | parsefail++; |
82b4a662 | 380 | continue; |
381 | } | |
a640a9d4 | 382 | } |
82b4a662 | 383 | else { // non-DNS ip |
384 | nonDNSIP++; | |
a640a9d4 BH |
385 | } |
386 | } | |
82b4a662 | 387 | cout<<"PCAP contained "<<pr.d_correctpackets<<" correct packets, "<<pr.d_runts<<" runts, "<< pr.d_oversized<<" oversize, "<<pr.d_nonetheripudp<<" non-UDP.\n"; |
388 | ||
a640a9d4 | 389 | } |
ef890b79 | 390 | |
391 | /* | |
392 | cout<<"Open when done: "<<endl; | |
393 | for(const auto& a : statmap) { | |
394 | cout<<a.first<<": qcount="<<a.second.d_qcount<<", answercount="<<a.second.d_answercount<<endl; | |
395 | } | |
396 | */ | |
bb85386d | 397 | |
a5d9353e | 398 | cout<<"Timespan: "<<(highestTime-lowestTime)/3600.0<<" hours"<<endl; |
1aac6e17 | 399 | |
ef890b79 | 400 | cout<<nonDNSIP<<" non-DNS UDP, "<<dnserrors<<" dns decoding errors, "<<parsefail<<" packets failed to parse"<<endl; |
ad79b1b5 | 401 | cout<<"Ignored fragment packets: "<<fragmented<<endl; |
d2d8cafd | 402 | cout<<"Dropped DNS packets based on recursion-desired filter: "<<rdFilterMismatch<<endl; |
7211d47b | 403 | if(!filtername.empty()) |
404 | cout <<"Dropped DNS packets because not part of '"<<filtername<<"': "<<nameMismatch << endl; | |
d2d8cafd | 405 | cout<<"DNS IPv4: "<<ipv4DNSPackets<<" packets, IPv6: "<<ipv6DNSPackets<<" packets"<<endl; |
ce9309b7 | 406 | cout<<"Questions: "<<queries<<", answers: "<<answers<<endl; |
ef890b79 | 407 | cout<<"Reuses of same state entry: "<<reuses<<endl; |
a640a9d4 | 408 | unsigned int unanswered=0; |
ad79b1b5 | 409 | |
410 | ||
411 | // ofstream openf("openf"); | |
a640a9d4 | 412 | for(statmap_t::const_iterator i=statmap.begin(); i!=statmap.end(); ++i) { |
7aba88c0 | 413 | if(!i->second.d_answercount) { |
a640a9d4 | 414 | unanswered++; |
7aba88c0 | 415 | } |
ad79b1b5 | 416 | //openf<< i->first.d_source.toStringWithPort()<<' ' <<i->first.d_dest.toStringWithPort()<<' '<<i->first.d_id<<' '<<i->first.d_qname <<" " <<i->first.d_qtype<< " "<<i->second.d_qcount <<" " <<i->second.d_answercount<<endl; |
a640a9d4 BH |
417 | } |
418 | ||
a5d9353e | 419 | cout<< boost::format("%d (%.02f%% of all) queries did not request recursion") % nonRDQueries % ((nonRDQueries*100.0)/queries) << endl; |
012de544 | 420 | cout<< rdNonRAAnswers << " answers had recursion desired bit set, but recursion available=0 (for "<<rdnonra.size()<<" remotes)"<<endl; |
a5d9353e | 421 | cout<<statmap.size()<<" queries went unanswered, of which "<< statmap.size()-unanswered<<" were answered on exact retransmit"<<endl; |
422 | cout<<untracked<<" responses could not be matched to questions"<<endl; | |
168eb01c | 423 | cout<<edns <<" questions requested EDNS processing, do=1: "<<dnssecOK<<", ad=1: "<<dnssecAD<<", cd=1: "<<dnssecCD<<endl; |
ce9309b7 | 424 | |
425 | if(answers) { | |
426 | cout<<(boost::format("%1% %|25t|%2%") % "Rcode" % "Count\n"); | |
427 | for(rcodes_t::const_iterator i=rcodes.begin(); i!=rcodes.end(); ++i) | |
428 | cout<<(boost::format("%s %|25t|%d %|35t|(%.1f%%)") % RCode::to_s(i->first) % i->second % (i->second*100.0/answers))<<endl; | |
429 | } | |
a640a9d4 BH |
430 | |
431 | uint32_t sum=0; | |
1ffb2c7f | 432 | // ofstream stats("stats"); |
e075bae3 | 433 | uint32_t totpairs=0; |
a640a9d4 BH |
434 | double tottime=0; |
435 | for(cumul_t::const_iterator i=cumul.begin(); i!=cumul.end(); ++i) { | |
1ffb2c7f | 436 | // stats<<i->first<<"\t"<<(sum+=i->second)<<"\n"; |
e075bae3 | 437 | totpairs+=i->second; |
a640a9d4 BH |
438 | tottime+=i->first*i->second; |
439 | } | |
bb85386d | 440 | |
a640a9d4 BH |
441 | typedef map<uint32_t, bool> done_t; |
442 | done_t done; | |
ef890b79 | 443 | for(auto a : {50, 100, 200, 300, 400, 800, 1000, 2000, 4000, 8000, 32000, 64000, 256000, 1024000, 2048000}) |
444 | done[a]=false; | |
a640a9d4 | 445 | |
843c7356 | 446 | cout.setf(std::ios::fixed); |
ef890b79 | 447 | cout.precision(4); |
a640a9d4 | 448 | sum=0; |
ef890b79 | 449 | |
75709ae8 | 450 | #if HAVE_BOOST_GE_148 |
ef890b79 | 451 | if(g_vm.count("log-histogram")) { |
452 | string fname = g_vm["stats-dir"].as<string>()+"/log-histogram"; | |
453 | ofstream loglog(fname); | |
454 | if(!loglog) | |
455 | throw runtime_error("Unable to write statistics to "+fname); | |
456 | ||
457 | writeLogHistogramFile(cumul, loglog); | |
458 | } | |
459 | ||
460 | if(g_vm.count("full-histogram")) { | |
461 | string fname=g_vm["stats-dir"].as<string>()+"/full-histogram"; | |
462 | ofstream loglog(fname); | |
463 | if(!loglog) | |
464 | throw runtime_error("Unable to write statistics to "+fname); | |
465 | writeFullHistogramFile(cumul, g_vm["full-histogram"].as<double>(), loglog); | |
466 | } | |
75709ae8 | 467 | #endif |
ef890b79 | 468 | |
bb85386d | 469 | |
ef890b79 | 470 | sum=0; |
a640a9d4 | 471 | double lastperc=0, perc=0; |
e075bae3 | 472 | uint64_t lastsum=0; |
a640a9d4 | 473 | |
a9edfc57 | 474 | for(cumul_t::const_iterator i=cumul.begin(); i!=cumul.end(); ++i) { |
e075bae3 | 475 | for(done_t::iterator j=done.begin(); j!=done.end(); ++j) { |
1aac6e17 | 476 | if(!j->second && i->first > j->first) { |
4957a608 BH |
477 | j->second=true; |
478 | ||
e075bae3 | 479 | perc=sum*100.0/totpairs; |
4957a608 | 480 | if(j->first < 1024) |
35f4f574 | 481 | cout<< perc <<"% of questions answered within " << j->first << " us ("; |
4957a608 | 482 | else |
9273c94d | 483 | cout<< perc <<"% of questions answered within " << j->first/1000.0 << " ms ("; |
bb85386d | 484 | |
4957a608 | 485 | cout<<perc-lastperc<<"%)\n"; |
e075bae3 | 486 | lastperc=sum*100.0/totpairs; |
487 | lastsum=sum; | |
1aac6e17 | 488 | } |
e075bae3 | 489 | } |
a9edfc57 | 490 | sum+=i->second; |
491 | } | |
492 | ||
493 | for(auto j = done.begin(); j != done.end(); ++j) { | |
494 | if(!j->second) { | |
495 | perc=sum*100.0/totpairs; | |
496 | if(j->first < 1024) | |
35f4f574 | 497 | cout<< perc <<"% of questions answered within " << j->first << " us ("; |
a9edfc57 | 498 | else |
9273c94d | 499 | cout<< perc <<"% of questions answered within " << j->first/1000.0 << " ms ("; |
bb85386d | 500 | |
a9edfc57 | 501 | cout<<perc-lastperc<<"%)\n"; |
502 | lastperc=sum*100.0/totpairs; | |
503 | lastsum=sum; | |
504 | break; | |
505 | } | |
a640a9d4 | 506 | } |
bb85386d | 507 | |
e075bae3 | 508 | cout<< (totpairs-lastsum)<<" responses ("<<((totpairs-lastsum)*100.0/answers) <<"%) older than "<< (done.rbegin()->first/1000000.0) <<" seconds"<<endl; |
509 | if(totpairs) | |
35f4f574 | 510 | cout<<"Average non-late response time: "<<tottime/totpairs<<" us"<<endl; |
9a07e342 | 511 | |
512 | if(!g_vm["load-stats"].as<string>().empty()) { | |
513 | ofstream load(g_vm["load-stats"].as<string>().c_str()); | |
bb85386d | 514 | if(!load) |
9a07e342 | 515 | throw runtime_error("Error writing load statistics to "+g_vm["load-stats"].as<string>()); |
ef7cd021 | 516 | for(pcounts_t::value_type& val : pcounts) { |
bb85386d | 517 | load<<val.first<<'\t'<<val.second.questions<<'\t'<<val.second.answers<<'\t'<<val.second.outstanding<<'\n'; |
9a07e342 | 518 | } |
519 | } | |
ce9309b7 | 520 | |
521 | ||
522 | cout<<"Saw questions from "<<requestors.size()<<" distinct remotes, answers to "<<recipients.size()<<endl; | |
523 | ofstream remotes("remotes"); | |
ef7cd021 | 524 | for(const ComboAddress& rem : requestors) { |
ce9309b7 | 525 | remotes<<rem.toString()<<'\n'; |
526 | } | |
527 | ||
528 | vector<ComboAddress> diff; | |
6264512b | 529 | set_difference(requestors.begin(), requestors.end(), recipients.begin(), recipients.end(), back_inserter(diff), ComboAddress::addressOnlyLessThan()); |
012de544 | 530 | cout<<"Saw "<<diff.size()<<" unique remotes asking questions, but not getting RA answers"<<endl; |
bb85386d | 531 | |
ce9309b7 | 532 | ofstream ignored("ignored"); |
ef7cd021 | 533 | for(const ComboAddress& rem : diff) { |
ce9309b7 | 534 | ignored<<rem.toString()<<'\n'; |
535 | } | |
536 | ofstream rdnonrafs("rdnonra"); | |
ef7cd021 | 537 | for(const ComboAddress& rem : rdnonra) { |
ce9309b7 | 538 | rdnonrafs<<rem.toString()<<'\n'; |
539 | } | |
9ad511a7 | 540 | |
541 | if(doServFailTree) { | |
542 | StatNode::Stat node; | |
543 | root.visit(visitor, node); | |
544 | } | |
545 | ||
a640a9d4 | 546 | } |
5172cb78 | 547 | catch(std::exception& e) |
a640a9d4 BH |
548 | { |
549 | cerr<<"Fatal: "<<e.what()<<endl; | |
550 | } |