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