]>
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 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
a7f7d29f RG |
25 | |
26 | #include <atomic> | |
7d8a5de7 | 27 | #include <iostream> |
a7f7d29f RG |
28 | #include <fstream> |
29 | #include <memory> | |
30 | #include <poll.h> | |
31 | #include <thread> | |
32 | ||
33 | #include <boost/program_options.hpp> | |
34 | ||
35 | #include "dns_random.hh" | |
7d8a5de7 | 36 | #include "dnsparser.hh" |
7d8a5de7 | 37 | #include "dnswriter.hh" |
38 | #include "dnsrecords.hh" | |
a7f7d29f RG |
39 | #include "ednsoptions.hh" |
40 | #include "ednssubnet.hh" | |
41 | #include "misc.hh" | |
42 | #include "sstuff.hh" | |
7d8a5de7 | 43 | #include "statbag.hh" |
a7f7d29f | 44 | |
7d8a5de7 | 45 | using std::thread; |
c1e527e3 | 46 | using std::unique_ptr; |
7d8a5de7 | 47 | |
48 | StatBag S; | |
49 | ||
325e59e7 RG |
50 | static std::atomic<unsigned int> g_recvcounter, g_recvbytes; |
51 | static volatile bool g_done; | |
7d8a5de7 | 52 | |
c7c8bdee | 53 | namespace po = boost::program_options; |
325e59e7 RG |
54 | static po::variables_map g_vm; |
55 | ||
56 | static bool g_quiet; | |
c7c8bdee | 57 | |
a7f7d29f | 58 | static void* recvThread(const vector<Socket*>* sockets) |
7d8a5de7 | 59 | { |
c1e527e3 | 60 | vector<pollfd> rfds, fds; |
61 | for(const auto& s : *sockets) { | |
62 | struct pollfd pfd; | |
63 | pfd.fd = s->getHandle(); | |
64 | pfd.events = POLLIN; | |
65 | pfd.revents = 0; | |
66 | rfds.push_back(pfd); | |
67 | } | |
68 | ||
69 | int err; | |
70 | ||
63fd0bc9 | 71 | #if HAVE_RECVMMSG |
c1e527e3 | 72 | vector<struct mmsghdr> buf(100); |
73 | for(auto& m : buf) { | |
74 | fillMSGHdr(&m.msg_hdr, new struct iovec, new char[512], 512, new char[1500], 1500, new ComboAddress("127.0.0.1")); | |
75 | } | |
63fd0bc9 OM |
76 | #else |
77 | struct msghdr buf; | |
78 | fillMSGHdr(&buf, new struct iovec, new char[512], 512, new char[1500], 1500, new ComboAddress("127.0.0.1")); | |
79 | #endif | |
c1e527e3 | 80 | |
7d8a5de7 | 81 | while(!g_done) { |
c1e527e3 | 82 | fds=rfds; |
83 | ||
84 | err = poll(&fds[0], fds.size(), -1); | |
63fd0bc9 OM |
85 | if (err < 0) { |
86 | if (errno == EINTR) | |
87 | continue; | |
c1e527e3 | 88 | unixDie("Unable to poll for new UDP events"); |
63fd0bc9 OM |
89 | } |
90 | ||
28c266da | 91 | for(auto &pfd : fds) { |
63fd0bc9 OM |
92 | if (pfd.revents & POLLIN) { |
93 | #if HAVE_RECVMMSG | |
94 | if ((err=recvmmsg(pfd.fd, &buf[0], buf.size(), MSG_WAITFORONE, 0)) < 0 ) { | |
95 | if(errno != EAGAIN) | |
3252abef | 96 | unixDie("recvmmsg"); |
63fd0bc9 OM |
97 | continue; |
98 | } | |
99 | g_recvcounter+=err; | |
100 | for(int n=0; n < err; ++n) | |
3252abef | 101 | g_recvbytes += buf[n].msg_len; |
63fd0bc9 OM |
102 | #else |
103 | if ((err = recvmsg(pfd.fd, &buf, 0)) < 0) { | |
104 | if (errno != EAGAIN) | |
3252abef | 105 | unixDie("recvmsg"); |
63fd0bc9 OM |
106 | continue; |
107 | } | |
108 | g_recvcounter++; | |
109 | for (int i = 0; i < buf.msg_iovlen; i++) | |
110 | g_recvbytes += buf.msg_iov[i].iov_len; | |
111 | #endif | |
c1e527e3 | 112 | } |
7d8a5de7 | 113 | } |
7d8a5de7 | 114 | } |
115 | return 0; | |
116 | } | |
117 | ||
a7f7d29f | 118 | static void setSocketBuffer(int fd, int optname, uint32_t size) |
7d8a5de7 | 119 | { |
120 | uint32_t psize=0; | |
121 | socklen_t len=sizeof(psize); | |
122 | ||
123 | if(!getsockopt(fd, SOL_SOCKET, optname, (char*)&psize, &len) && psize > size) { | |
325e59e7 RG |
124 | if (!g_quiet) { |
125 | cerr<<"Not decreasing socket buffer size from "<<psize<<" to "<<size<<endl; | |
126 | } | |
7d8a5de7 | 127 | return; |
128 | } | |
129 | ||
325e59e7 RG |
130 | if (setsockopt(fd, SOL_SOCKET, optname, (char*)&size, sizeof(size)) < 0 ) { |
131 | if (!g_quiet) { | |
132 | cerr<<"Warning: unable to raise socket buffer size to "<<size<<": "<<strerror(errno)<<endl; | |
133 | } | |
134 | } | |
7d8a5de7 | 135 | } |
136 | ||
137 | ||
138 | static void setSocketReceiveBuffer(int fd, uint32_t size) | |
139 | { | |
140 | setSocketBuffer(fd, SO_RCVBUF, size); | |
141 | } | |
142 | ||
143 | static void setSocketSendBuffer(int fd, uint32_t size) | |
144 | { | |
145 | setSocketBuffer(fd, SO_SNDBUF, size); | |
146 | } | |
147 | ||
a7f7d29f RG |
148 | static ComboAddress getRandomAddressFromRange(const Netmask& ecsRange) |
149 | { | |
150 | ComboAddress result = ecsRange.getMaskedNetwork(); | |
151 | uint8_t bits = ecsRange.getBits(); | |
152 | uint32_t mod = 1 << (32 - bits); | |
153 | result.sin4.sin_addr.s_addr = result.sin4.sin_addr.s_addr + ntohl(dns_random(mod)); | |
154 | return result; | |
155 | } | |
156 | ||
157 | static void replaceEDNSClientSubnet(vector<uint8_t>* packet, const Netmask& ecsRange) | |
158 | { | |
159 | /* the last 4 bytes of the packet are the IPv4 address */ | |
160 | ComboAddress rnd = getRandomAddressFromRange(ecsRange); | |
161 | uint32_t addr = rnd.sin4.sin_addr.s_addr; | |
162 | ||
163 | const auto packetSize = packet->size(); | |
164 | if (packetSize < sizeof(addr)) { | |
165 | return; | |
166 | } | |
167 | ||
168 | memcpy(&packet->at(packetSize - sizeof(addr)), &addr, sizeof(addr)); | |
169 | } | |
170 | ||
171 | static void sendPackets(const vector<Socket*>* sockets, const vector<vector<uint8_t>* >& packets, int qps, ComboAddress dest, const Netmask& ecsRange) | |
e9a27786 | 172 | { |
c1e527e3 | 173 | unsigned int burst=100; |
9e8a59fc | 174 | const auto nsecPerBurst=1*(unsigned long)(burst*1000000000.0/qps); |
e9a27786 | 175 | struct timespec nsec; |
176 | nsec.tv_sec=0; | |
9e8a59fc | 177 | nsec.tv_nsec=0; |
e9a27786 | 178 | int count=0; |
9e8a59fc KW |
179 | unsigned int nBursts=0; |
180 | DTime dt; | |
181 | dt.set(); | |
e9a27786 | 182 | |
c1e527e3 | 183 | struct Unit { |
c1e527e3 | 184 | struct msghdr msgh; |
185 | struct iovec iov; | |
186 | char cbuf[256]; | |
c1e527e3 | 187 | }; |
ea550a82 | 188 | vector<unique_ptr<Unit> > units; |
189 | int ret; | |
c1e527e3 | 190 | |
fa5958af | 191 | for(const auto& p : packets) { |
e9a27786 | 192 | count++; |
e9a27786 | 193 | |
19f9117b | 194 | Unit u; |
c1e527e3 | 195 | |
a7f7d29f RG |
196 | if (!ecsRange.empty()) { |
197 | replaceEDNSClientSubnet(p, ecsRange); | |
198 | } | |
199 | ||
63fd0bc9 | 200 | fillMSGHdr(&u.msgh, &u.iov, nullptr, 0, (char*)&(*p)[0], p->size(), &dest); |
19f9117b | 201 | if((ret=sendmsg((*sockets)[count % sockets->size()]->getHandle(), |
202 | &u.msgh, 0))) | |
203 | if(ret < 0) | |
63fd0bc9 | 204 | unixDie("sendmsg"); |
19f9117b | 205 | |
206 | ||
9e8a59fc KW |
207 | if(!(count%burst)) { |
208 | nBursts++; | |
209 | // Calculate the time in nsec we need to sleep to the next burst. | |
210 | // If this is negative, it means that we are not achieving the requested | |
211 | // target rate, in which case we skip the sleep. | |
212 | int toSleep = nBursts*nsecPerBurst - 1000*dt.udiffNoReset(); | |
213 | if (toSleep > 0) { | |
214 | nsec.tv_nsec = toSleep; | |
215 | nanosleep(&nsec, 0); | |
216 | } | |
217 | } | |
e9a27786 | 218 | } |
219 | } | |
220 | ||
a7f7d29f | 221 | static void usage(po::options_description &desc) { |
1fe98901 | 222 | cerr<<"Syntax: calidns [OPTIONS] QUERY_FILE DESTINATION INITIAL_QPS HITRATE"<<endl; |
c7c8bdee | 223 | cerr<<desc<<endl; |
32823984 | 224 | } |
7d8a5de7 | 225 | |
fa5958af | 226 | /* |
227 | New plan. Set cache hit percentage, which we achieve on a per second basis. | |
228 | So we start with 10000 qps for example, and for 90% cache hit ratio means | |
229 | we take 1000 unique queries and each send them 10 times. | |
230 | ||
231 | We then move the 1000 unique queries to the 'known' pool. | |
232 | ||
233 | For the next second, say 20000 qps, we know we are going to need 2000 new queries, | |
234 | so we take 2000 from the unknown pool. Then we need 18000 cache hits. We can get 1000 from | |
235 | the known pool, leaving us down 17000. Or, we have 3000 in total now and we need 2000. We simply | |
236 | repeat the 3000 mix we have ~7 times. The 2000 can now go to the known pool too. | |
237 | ||
238 | For the next second, say 30000 qps, we'll need 3000 cache misses, which we get from | |
239 | the unknown pool. To this we add 3000 queries from the known pool. Next up we repeat this batch 5 | |
240 | times. | |
241 | ||
242 | In general the algorithm therefore is: | |
243 | ||
244 | 1) Calculate number of cache misses required, get them from the unknown pool | |
245 | 2) Move those to the known pool | |
246 | 3) Fill up to amount of queries we need with random picks from the known pool | |
247 | ||
248 | */ | |
7d8a5de7 | 249 | |
250 | int main(int argc, char** argv) | |
251 | try | |
252 | { | |
c7c8bdee PL |
253 | po::options_description desc("Options"); |
254 | desc.add_options() | |
255 | ("help,h", "Show this helpful message") | |
256 | ("version", "Show the version number") | |
a7f7d29f | 257 | ("ecs", po::value<string>(), "Add EDNS Client Subnet option to outgoing queries using random addresses from the specified range (IPv4 only)") |
a0149ecc | 258 | ("ecs-from-file", "Read IP or subnet values from the query file and add them as EDNS Client Subnet options to outgoing queries") |
c7c8bdee | 259 | ("increment", po::value<float>()->default_value(1.1), "Set the factor to increase the QPS load per run") |
f3f8331c | 260 | ("maximum-qps", po::value<uint32_t>(), "Stop incrementing once this rate has been reached, to provide a stable load") |
ce39630b | 261 | ("minimum-success-rate", po::value<double>()->default_value(0), "Stop the test as soon as the success rate drops below this value, in percent") |
4038b8d6 | 262 | ("plot-file", po::value<string>(), "Write results to the specific file") |
23165000 | 263 | ("quiet", "Whether to run quietly, outputting only the maximum QPS reached. This option is mostly useful when used with --minimum-success-rate") |
c7c8bdee PL |
264 | ("want-recursion", "Set the Recursion Desired flag on queries"); |
265 | po::options_description alloptions; | |
266 | po::options_description hidden("hidden options"); | |
267 | hidden.add_options() | |
268 | ("query-file", po::value<string>(), "File with queries") | |
269 | ("destination", po::value<string>(), "Destination address") | |
d9ea45fc | 270 | ("initial-qps", po::value<uint32_t>(), "Initial number of queries per second") |
c7c8bdee PL |
271 | ("hitrate", po::value<double>(), "Aim this percent cache hitrate"); |
272 | ||
273 | alloptions.add(desc).add(hidden); | |
274 | po::positional_options_description p; | |
275 | p.add("query-file", 1); | |
276 | p.add("destination", 1); | |
d9ea45fc | 277 | p.add("initial-qps", 1); |
c7c8bdee PL |
278 | p.add("hitrate", 1); |
279 | ||
280 | po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm); | |
281 | po::notify(g_vm); | |
282 | ||
283 | if (g_vm.count("help")) { | |
284 | usage(desc); | |
285 | return EXIT_SUCCESS; | |
1fe98901 | 286 | } |
32823984 | 287 | |
c7c8bdee PL |
288 | if (g_vm.count("version")) { |
289 | cerr<<"calidns "<<VERSION<<endl; | |
290 | return EXIT_SUCCESS; | |
291 | } | |
1fe98901 | 292 | |
d9ea45fc | 293 | if (!(g_vm.count("query-file") && g_vm.count("destination") && g_vm.count("initial-qps") && g_vm.count("hitrate"))) { |
c7c8bdee PL |
294 | usage(desc); |
295 | return EXIT_FAILURE; | |
1fe98901 PL |
296 | } |
297 | ||
c7c8bdee PL |
298 | float increment = 1.1; |
299 | try { | |
300 | increment = g_vm["increment"].as<float>(); | |
b5955f7a | 301 | } |
c7c8bdee PL |
302 | catch(...) { |
303 | } | |
304 | ||
305 | bool wantRecursion = g_vm.count("want-recursion"); | |
a0149ecc | 306 | bool useECSFromFile = g_vm.count("ecs-from-file"); |
325e59e7 | 307 | g_quiet = g_vm.count("quiet"); |
32823984 | 308 | |
0a76eab7 PL |
309 | double hitrate = g_vm["hitrate"].as<double>(); |
310 | if (hitrate > 100 || hitrate < 0) { | |
311 | cerr<<"hitrate must be between 0 and 100, not "<<hitrate<<endl; | |
312 | return EXIT_FAILURE; | |
313 | } | |
314 | hitrate /= 100; | |
d9ea45fc | 315 | uint32_t qpsstart = g_vm["initial-qps"].as<uint32_t>(); |
1fe98901 | 316 | |
f3f8331c RG |
317 | uint32_t maximumQps = std::numeric_limits<uint32_t>::max(); |
318 | if (g_vm.count("maximum-qps")) { | |
319 | maximumQps = g_vm["maximum-qps"].as<uint32_t>(); | |
320 | } | |
321 | ||
ce39630b RG |
322 | double minimumSuccessRate = g_vm["minimum-success-rate"].as<double>(); |
323 | if (minimumSuccessRate > 100.0 || minimumSuccessRate < 0.0) { | |
324 | cerr<<"Minimum success rate must be between 0 and 100, not "<<minimumSuccessRate<<endl; | |
325 | return EXIT_FAILURE; | |
326 | } | |
327 | ||
a7f7d29f RG |
328 | Netmask ecsRange; |
329 | if (g_vm.count("ecs")) { | |
330 | dns_random_init("0123456789abcdef"); | |
331 | ||
332 | try { | |
333 | ecsRange = Netmask(g_vm["ecs"].as<string>()); | |
334 | if (!ecsRange.empty()) { | |
335 | ||
336 | if (!ecsRange.isIpv4()) { | |
337 | cerr<<"Only IPv4 ranges are supported for ECS at the moment!"<<endl; | |
338 | return EXIT_FAILURE; | |
339 | } | |
340 | ||
325e59e7 | 341 | if (!g_quiet) { |
23165000 RG |
342 | cout<<"Adding ECS option to outgoing queries with random addresses from the "<<ecsRange.toString()<<" range"<<endl; |
343 | } | |
a7f7d29f RG |
344 | } |
345 | } | |
346 | catch (const NetmaskException& e) { | |
347 | cerr<<"Error while parsing the ECS netmask: "<<e.reason<<endl; | |
348 | return EXIT_FAILURE; | |
349 | } | |
350 | } | |
351 | ||
c7c8bdee PL |
352 | struct sched_param param; |
353 | param.sched_priority=99; | |
1fe98901 | 354 | |
63fd0bc9 | 355 | #if HAVE_SCHED_SETSCHEDULER |
ce39630b | 356 | if(sched_setscheduler(0, SCHED_FIFO, ¶m) < 0) { |
325e59e7 RG |
357 | if (!g_quiet) { |
358 | cerr<<"Unable to set SCHED_FIFO: "<<strerror(errno)<<endl; | |
359 | } | |
ce39630b | 360 | } |
63fd0bc9 | 361 | #endif |
e9a27786 | 362 | |
c7c8bdee | 363 | ifstream ifs(g_vm["query-file"].as<string>()); |
7d8a5de7 | 364 | string line; |
365 | reportAllTypes(); | |
fa5958af | 366 | vector<std::shared_ptr<vector<uint8_t> > > unknown, known; |
9a0a974e RG |
367 | std::vector<std::string> fields; |
368 | fields.reserve(3); | |
369 | ||
7d8a5de7 | 370 | while(getline(ifs, line)) { |
371 | vector<uint8_t> packet; | |
a7f7d29f | 372 | DNSPacketWriter::optvect_t ednsOptions; |
7d8a5de7 | 373 | boost::trim(line); |
46a3f566 RG |
374 | if (line.empty() || line.at(0) == '#') { |
375 | continue; | |
376 | } | |
a0149ecc | 377 | |
9a0a974e RG |
378 | fields.clear(); |
379 | stringtok(fields, line, "\t "); | |
380 | if ((useECSFromFile && fields.size() < 3) || fields.size() < 2) { | |
381 | cerr<<"Skipping invalid line '"<<line<<", it does not contain enough values"<<endl; | |
382 | continue; | |
383 | } | |
384 | ||
385 | const std::string& qname = fields.at(0); | |
386 | const std::string& qtype = fields.at(1); | |
a0149ecc RG |
387 | std::string subnet; |
388 | ||
9a0a974e RG |
389 | if (useECSFromFile) { |
390 | subnet = fields.at(2); | |
a0149ecc RG |
391 | } |
392 | ||
393 | DNSPacketWriter pw(packet, DNSName(qname), DNSRecordContent::TypeToNumber(qtype)); | |
1fe98901 | 394 | pw.getHeader()->rd=wantRecursion; |
b51ef4f9 | 395 | pw.getHeader()->id=dns_random(UINT16_MAX); |
a7f7d29f | 396 | |
a0149ecc | 397 | if(!subnet.empty() || !ecsRange.empty()) { |
a7f7d29f | 398 | EDNSSubnetOpts opt; |
a0149ecc | 399 | opt.source = Netmask(subnet.empty() ? "0.0.0.0/32" : subnet); |
a7f7d29f RG |
400 | ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt))); |
401 | } | |
402 | ||
403 | if(!ednsOptions.empty() || pw.getHeader()->id % 2) { | |
404 | pw.addOpt(1500, 0, EDNSOpts::DNSSECOK, ednsOptions); | |
405 | pw.commit(); | |
3df2e6c4 | 406 | } |
fa5958af | 407 | unknown.emplace_back(std::make_shared<vector<uint8_t>>(packet)); |
7d8a5de7 | 408 | } |
fa5958af | 409 | random_shuffle(unknown.begin(), unknown.end()); |
325e59e7 | 410 | if (!g_quiet) { |
23165000 RG |
411 | cout<<"Generated "<<unknown.size()<<" ready to use queries"<<endl; |
412 | } | |
7d8a5de7 | 413 | |
e9a27786 | 414 | vector<Socket*> sockets; |
b807ae7b PL |
415 | ComboAddress dest; |
416 | try { | |
417 | dest = ComboAddress(g_vm["destination"].as<string>(), 53); | |
418 | } | |
419 | catch (PDNSException &e) { | |
420 | cerr<<e.reason<<endl; | |
421 | return EXIT_FAILURE; | |
422 | } | |
c1e527e3 | 423 | for(int i=0; i < 24; ++i) { |
7f363f60 | 424 | Socket *sock = new Socket(dest.sin4.sin_family, SOCK_DGRAM); |
c1e527e3 | 425 | // sock->connect(dest); |
e9a27786 | 426 | setSocketSendBuffer(sock->getHandle(), 2000000); |
427 | setSocketReceiveBuffer(sock->getHandle(), 2000000); | |
428 | sockets.push_back(sock); | |
c1e527e3 | 429 | } |
430 | new thread(recvThread, &sockets); | |
f3f8331c | 431 | uint32_t qps; |
7d8a5de7 | 432 | |
4038b8d6 RG |
433 | ofstream plot; |
434 | if (g_vm.count("plot-file")) { | |
435 | plot.open(g_vm["plot-file"].as<string>()); | |
436 | if (!plot) { | |
437 | cerr<<"Error opening "<<g_vm["plot-file"].as<string>()<<" for writing: "<<strerror(errno)<<endl; | |
438 | return EXIT_FAILURE; | |
439 | } | |
440 | } | |
441 | ||
ce39630b RG |
442 | double bestQPS = 0.0; |
443 | double bestPerfectQPS = 0.0; | |
444 | ||
f3f8331c | 445 | for(qps=qpsstart;;) { |
fa5958af | 446 | double seconds=1; |
325e59e7 | 447 | if (!g_quiet) { |
23165000 RG |
448 | cout<<"Aiming at "<<qps<< "qps (RD="<<wantRecursion<<") for "<<seconds<<" seconds at cache hitrate "<<100.0*hitrate<<"%"; |
449 | } | |
fa5958af | 450 | unsigned int misses=(1-hitrate)*qps*seconds; |
451 | unsigned int total=qps*seconds; | |
b4f5799b RG |
452 | if (misses == 0) { |
453 | misses = 1; | |
454 | } | |
325e59e7 | 455 | if (!g_quiet) { |
23165000 RG |
456 | cout<<", need "<<misses<<" misses, "<<total<<" queries, have "<<unknown.size()<<" unknown left!"<<endl; |
457 | } | |
fa5958af | 458 | |
b4f5799b RG |
459 | if (misses > unknown.size()) { |
460 | cerr<<"Not enough queries remaining (need at least "<<misses<<" and got "<<unknown.size()<<", please add more to the query file), exiting."<<endl; | |
ce39630b | 461 | return EXIT_FAILURE; |
b4f5799b | 462 | } |
fa5958af | 463 | vector<vector<uint8_t>*> toSend; |
464 | unsigned int n; | |
465 | for(n=0; n < misses; ++n) { | |
466 | auto ptr=unknown.back(); | |
467 | unknown.pop_back(); | |
468 | toSend.push_back(ptr.get()); | |
469 | known.push_back(ptr); | |
470 | } | |
471 | for(;n < total; ++n) { | |
b51ef4f9 | 472 | toSend.push_back(known[dns_random(known.size())].get()); |
fa5958af | 473 | } |
474 | random_shuffle(toSend.begin(), toSend.end()); | |
e9a27786 | 475 | g_recvcounter.store(0); |
3df2e6c4 | 476 | g_recvbytes=0; |
e9a27786 | 477 | DTime dt; |
478 | dt.set(); | |
479 | ||
a7f7d29f | 480 | sendPackets(&sockets, toSend, qps, dest, ecsRange); |
e9a27786 | 481 | |
ce39630b RG |
482 | const auto udiff = dt.udiffNoReset(); |
483 | const auto realqps=toSend.size()/(udiff/1000000.0); | |
325e59e7 | 484 | if (!g_quiet) { |
23165000 RG |
485 | cout<<"Achieved "<<realqps<<" qps over "<< udiff/1000000.0<<" seconds"<<endl; |
486 | } | |
e9a27786 | 487 | |
488 | usleep(50000); | |
ce39630b RG |
489 | const auto received = g_recvcounter.load(); |
490 | const auto udiffReceived = dt.udiff(); | |
491 | const auto realReceivedQPS = received/(udiffReceived/1000000.0); | |
492 | double perc=received*100.0/toSend.size(); | |
325e59e7 | 493 | if (!g_quiet) { |
23165000 RG |
494 | cout<<"Received "<<received<<" packets over "<< udiffReceived/1000000.0<<" seconds ("<<perc<<"%, adjusted received rate "<<realReceivedQPS<<" qps)"<<endl; |
495 | } | |
4038b8d6 RG |
496 | |
497 | if (plot) { | |
ce39630b | 498 | plot<<qps<<" "<<realqps<<" "<<perc<<" "<<received/(udiff/1000000.0)<<" " << 8*g_recvbytes.load()/(udiff/1000000.0)<<endl; |
4038b8d6 RG |
499 | plot.flush(); |
500 | } | |
501 | ||
f3f8331c RG |
502 | if (qps < maximumQps) { |
503 | qps *= increment; | |
504 | } | |
ce39630b RG |
505 | else { |
506 | qps = maximumQps; | |
507 | } | |
508 | ||
509 | if (minimumSuccessRate > 0.0 && perc < minimumSuccessRate) { | |
325e59e7 | 510 | if (g_quiet) { |
23165000 RG |
511 | cout<<bestQPS<<endl; |
512 | } | |
513 | else { | |
514 | cout<<"The latest success rate ("<<perc<<") dropped below the minimum success rate of "<<minimumSuccessRate<<", stopping."<<endl; | |
515 | cout<<"The final rate reached before failing was "<<bestQPS<<" qps (best rate at 100% was "<<bestPerfectQPS<<" qps)"<<endl; | |
516 | } | |
ce39630b RG |
517 | break; |
518 | } | |
519 | ||
520 | bestQPS = std::max(bestQPS, realReceivedQPS); | |
521 | if (perc >= 100.0) { | |
522 | bestPerfectQPS = std::max(bestPerfectQPS, realReceivedQPS); | |
523 | } | |
7d8a5de7 | 524 | } |
4038b8d6 RG |
525 | |
526 | if (plot) { | |
527 | plot.flush(); | |
528 | } | |
529 | ||
e9a27786 | 530 | // t1.detach(); |
7d8a5de7 | 531 | } |
532 | catch(std::exception& e) | |
533 | { | |
534 | cerr<<"Fatal error: "<<e.what()<<endl; | |
325e59e7 | 535 | return EXIT_FAILURE; |
7d8a5de7 | 536 | } |