]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnstcpbench.cc
Sphinx 1.8.0 seems broken, use any other version available instead
[thirdparty/pdns.git] / pdns / dnstcpbench.cc
CommitLineData
22f4bd59 1/*
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
4106b16e 25#include <boost/accumulators/statistics/median.hpp>
26#include <boost/accumulators/statistics/mean.hpp>
27#include <boost/accumulators/accumulators.hpp>
28
29#include <boost/accumulators/statistics.hpp>
30
a51b52c9 31#include "dnsparser.hh"
32#include "sstuff.hh"
33#include "misc.hh"
34#include "dnswriter.hh"
35#include "dnsrecords.hh"
36#include "statbag.hh"
a8f90a6a 37#include <netinet/tcp.h>
a51b52c9 38#include <boost/array.hpp>
36658df5 39#include <boost/program_options.hpp>
fa8fd4d2 40
a51b52c9 41
36658df5 42StatBag S;
43namespace po = boost::program_options;
4106b16e 44
36658df5 45po::variables_map g_vm;
46bool g_verbose;
a51b52c9 47bool g_onlyTCP;
a8f90a6a 48bool g_tcpNoDelay;
36658df5 49unsigned int g_timeoutMsec;
50AtomicCounter g_networkErrors, g_otherErrors, g_OK, g_truncates, g_authAnswers, g_timeOuts;
1d74012c 51ComboAddress g_dest;
f7896b52 52
4106b16e 53unsigned int makeUsec(const struct timeval& tv)
54{
55 return 1000000*tv.tv_sec + tv.tv_usec;
56}
57
22f4bd59 58/* On Linux, run echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
59 to prevent running out of free TCP ports */
60
61struct BenchQuery
62{
4106b16e 63 BenchQuery(const std::string& qname_, uint16_t qtype_) : qname(qname_), qtype(qtype_), udpUsec(0), tcpUsec(0), answerSecond(0) {}
22f4bd59 64 BenchQuery(){}
eaedd091 65 DNSName qname;
22f4bd59 66 uint16_t qtype;
4106b16e 67 uint32_t udpUsec, tcpUsec;
68 time_t answerSecond;
22f4bd59 69};
f7896b52 70
22f4bd59 71void doQuery(BenchQuery* q)
f7896b52 72try
a51b52c9 73{
74 vector<uint8_t> packet;
1d74012c 75 DNSPacketWriter pw(packet, q->qname, q->qtype);
36658df5 76 int res;
a51b52c9 77 string reply;
78
4106b16e 79 struct timeval tv, now;
80 gettimeofday(&tv, 0);
81
a51b52c9 82 if(!g_onlyTCP) {
528b753d 83 Socket udpsock(g_dest.sin4.sin_family, SOCK_DGRAM);
a51b52c9 84
1d74012c 85 udpsock.sendTo(string((char*)&*packet.begin(), (char*)&*packet.end()), g_dest);
a51b52c9 86 ComboAddress origin;
36658df5 87 res = waitForData(udpsock.getHandle(), 0, 1000 * g_timeoutMsec);
88 if(res < 0)
89 throw NetworkError("Error waiting for response");
90 if(!res) {
91 g_timeOuts++;
92 return;
93 }
94
a51b52c9 95 udpsock.recvFrom(reply, origin);
4106b16e 96
97 gettimeofday(&now, 0);
98 q->udpUsec = makeUsec(now - tv);
99 tv=now;
100
27c0050c 101 MOADNSParser mdp(false, reply);
a51b52c9 102 if(!mdp.d_header.tc)
103 return;
f7896b52 104 g_truncates++;
a51b52c9 105 }
106
528b753d 107 Socket sock(g_dest.sin4.sin_family, SOCK_STREAM);
f7896b52 108 int tmp=1;
109 if(setsockopt(sock.getHandle(),SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
110 throw runtime_error("Unable to set socket reuse: "+string(strerror(errno)));
a8f90a6a 111
112 if(g_tcpNoDelay && setsockopt(sock.getHandle(), IPPROTO_TCP, TCP_NODELAY,(char*)&tmp,sizeof tmp)<0)
113 throw runtime_error("Unable to set socket no delay: "+string(strerror(errno)));
a51b52c9 114
1d74012c 115 sock.connect(g_dest);
36658df5 116 uint16_t len = htons(packet.size());
117 string tcppacket((char*)& len, 2);
118 tcppacket.append((char*)&*packet.begin(), (char*)&*packet.end());
119
120 sock.writen(tcppacket);
121
122 res = waitForData(sock.getHandle(), 0, 1000 * g_timeoutMsec);
123 if(res < 0)
124 throw NetworkError("Error waiting for response");
125 if(!res) {
126 g_timeOuts++;
127 return;
128 }
a51b52c9 129
130 if(sock.read((char *) &len, 2) != 2)
3f81d239 131 throw PDNSException("tcp read failed");
a51b52c9 132
133 len=ntohs(len);
134 char *creply = new char[len];
135 int n=0;
136 int numread;
137 while(n<len) {
138 numread=sock.read(creply+n, len-n);
139 if(numread<0)
3f81d239 140 throw PDNSException("tcp read failed");
a51b52c9 141 n+=numread;
142 }
143
144 reply=string(creply, len);
145 delete[] creply;
146
4106b16e 147 gettimeofday(&now, 0);
148 q->tcpUsec = makeUsec(now - tv);
149 q->answerSecond = now.tv_sec;
150
27c0050c 151 MOADNSParser mdp(false, reply);
f7896b52 152 // cout<<"Had correct TCP/IP response, "<<mdp.d_answers.size()<<" answers, aabit="<<mdp.d_header.aa<<endl;
36658df5 153 if(mdp.d_header.aa)
154 g_authAnswers++;
f7896b52 155 g_OK++;
156}
157catch(NetworkError& ne)
158{
159 cerr<<"Network error: "<<ne.what()<<endl;
160 g_networkErrors++;
161}
162catch(...)
163{
164 g_otherErrors++;
a51b52c9 165}
166
167/* read queries from stdin, put in vector
168 launch n worker threads, each picks a query using AtomicCounter
169 If a worker reaches the end of its queue, it stops */
170
171AtomicCounter g_pos;
682549f2 172
22f4bd59 173vector<BenchQuery> g_queries;
1d74012c 174
682549f2 175static void* worker(void*)
a51b52c9 176{
a51b52c9 177 for(;;) {
f7896b52 178 unsigned int pos = g_pos++;
179 if(pos >= g_queries.size())
a51b52c9 180 break;
1d74012c 181
182 doQuery(&g_queries[pos]); // this is safe as long as nobody *inserts* to g_queries
a51b52c9 183 }
184 return 0;
185}
186
c4f20ff9
PL
187void usage(po::options_description &desc) {
188 cerr<<"Syntax: dnstcpbench REMOTE [PORT] < QUERIES"<<endl;
189 cerr<<"Where QUERIES is one query per line, format: qname qtype, just 1 space"<<endl;
190 cerr<<desc<<endl;
191}
192
a51b52c9 193int main(int argc, char** argv)
194try
195{
36658df5 196 po::options_description desc("Allowed options"), hidden, alloptions;
197 desc.add_options()
198 ("help,h", "produce help message")
c4f20ff9 199 ("version", "print version number")
36658df5 200 ("verbose,v", "be verbose")
201 ("udp-first,u", "try UDP first")
74f463af 202 ("file,f", po::value<string>(), "source file - if not specified, defaults to stdin")
a8f90a6a 203 ("tcp-no-delay", po::value<bool>()->default_value(true), "use TCP_NODELAY socket option")
36658df5 204 ("timeout-msec", po::value<int>()->default_value(10), "wait for this amount of milliseconds for an answer")
205 ("workers", po::value<int>()->default_value(100), "number of parallel workers");
206
207 hidden.add_options()
208 ("remote-host", po::value<string>(), "remote-host")
209 ("remote-port", po::value<int>()->default_value(53), "remote-port");
210 alloptions.add(desc).add(hidden);
211
212 po::positional_options_description p;
213 p.add("remote-host", 1);
214 p.add("remote-port", 1);
215
216 po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
217 po::notify(g_vm);
c4f20ff9
PL
218
219 if(g_vm.count("version")) {
220 cerr<<"dnstcpbench "<<VERSION<<endl;
221 exit(EXIT_SUCCESS);
222 }
223
36658df5 224 if(g_vm.count("help")) {
c4f20ff9 225 usage(desc);
36658df5 226 exit(EXIT_SUCCESS);
227 }
a8f90a6a 228 g_tcpNoDelay = g_vm["tcp-no-delay"].as<bool>();
229
36658df5 230 g_onlyTCP = !g_vm.count("udp-first");
231 g_verbose = g_vm.count("verbose");
232 g_timeoutMsec = g_vm["timeout-msec"].as<int>();
233
a51b52c9 234 reportAllTypes();
f7896b52 235
36658df5 236 if(g_vm["remote-host"].empty()) {
c4f20ff9 237 usage(desc);
f7896b52 238 exit(EXIT_FAILURE);
239 }
f7896b52 240
36658df5 241 g_dest = ComboAddress(g_vm["remote-host"].as<string>().c_str(), g_vm["remote-port"].as<int>());
242
243 unsigned int numworkers=g_vm["workers"].as<int>();
244
245 if(g_verbose) {
246 cout<<"Sending queries to: "<<g_dest.toStringWithPort()<<endl;
247 cout<<"Attempting UDP first: " << (g_onlyTCP ? "no" : "yes") <<endl;
248 cout<<"Timeout: "<< g_timeoutMsec<<"msec"<<endl;
a8f90a6a 249 cout << "Using TCP_NODELAY: "<<g_tcpNoDelay<<endl;
36658df5 250 }
251
252
a51b52c9 253 pthread_t workers[numworkers];
254
74f463af 255 FILE* fp;
256 if(!g_vm.count("file"))
257 fp=fdopen(0, "r");
258 else {
259 fp=fopen(g_vm["file"].as<string>().c_str(), "r");
260 if(!fp)
261 unixDie("Unable to open "+g_vm["file"].as<string>()+" for input");
262 }
f7896b52 263 pair<string, string> q;
264 string line;
265 while(stringfgets(fp, line)) {
266 trim_right(line);
267 q=splitField(line, ' ');
22f4bd59 268 g_queries.push_back(BenchQuery(q.first, DNSRecordContent::TypeToNumber(q.second)));
a51b52c9 269 }
f7896b52 270 fclose(fp);
271
a51b52c9 272 for(unsigned int n = 0; n < numworkers; ++n) {
273 pthread_create(&workers[n], 0, worker, 0);
274 }
275 for(unsigned int n = 0; n < numworkers; ++n) {
276 void* status;
277 pthread_join(workers[n], &status);
278 }
4106b16e 279
280 using namespace boost::accumulators;
281 typedef accumulator_set<
40cd3e09 282 double
4106b16e 283 , stats<boost::accumulators::tag::median(with_p_square_quantile),
284 boost::accumulators::tag::mean(immediate)
285 >
286 > acc_t;
287
288 acc_t udpspeeds, tcpspeeds, qps;
289
290 typedef map<time_t, uint32_t> counts_t;
291 counts_t counts;
292
ef7cd021 293 for(const BenchQuery& bq : g_queries) {
4106b16e 294 counts[bq.answerSecond]++;
295 udpspeeds(bq.udpUsec);
296 tcpspeeds(bq.tcpUsec);
297 }
298
ef7cd021 299 for(const counts_t::value_type& val : counts) {
4106b16e 300 qps(val.second);
301 }
302
303 cout<<"Average qps: "<<mean(qps)<<", median qps: "<<median(qps)<<endl;
304 cout<<"Average UDP latency: "<<mean(udpspeeds)<<"usec, median: "<<median(udpspeeds)<<"usec"<<endl;
305 cout<<"Average TCP latency: "<<mean(tcpspeeds)<<"usec, median: "<<median(tcpspeeds)<<"usec"<<endl;
306
f7896b52 307 cout<<"OK: "<<g_OK<<", network errors: "<<g_networkErrors<<", other errors: "<<g_otherErrors<<endl;
36658df5 308 cout<<"Timeouts: "<<g_timeOuts<<endl;
309 cout<<"Truncateds: "<<g_truncates<<", auth answers: "<<g_authAnswers<<endl;
a51b52c9 310}
311catch(std::exception &e)
312{
313 cerr<<"Fatal: "<<e.what()<<endl;
314}