root/trunk/pdns/pdns/speedtest.cc @ 1689

Revision 1689, 19.1 KB (checked in by ahu, 3 years ago)

silence warnings, enable separate compilation

Line 
1#include "dnsparser.hh"
2#include "sstuff.hh"
3#include "misc.hh"
4#include "dnswriter.hh"
5#include "dnsrecords.hh"
6#include <boost/format.hpp>
7#include "config.h"
8#ifndef RECURSOR
9#include "statbag.hh"
10StatBag S;
11#endif
12
13volatile bool g_ret; // make sure the optimizer does not get too smart
14uint64_t g_totalRuns;
15
16volatile bool g_stop;
17
18void alarmHandler(int)
19{
20  g_stop=true;
21}
22
23template<typename C> void doRun(const C& cmd, int mseconds=100)
24{
25  struct itimerval it;
26  it.it_value.tv_sec=mseconds/1000;
27  it.it_value.tv_usec = 1000* (mseconds%1000);
28  it.it_interval.tv_sec=0;
29  it.it_interval.tv_usec=0;
30
31  signal(SIGVTALRM, alarmHandler);
32  setitimer(ITIMER_VIRTUAL, &it, 0);
33 
34  unsigned int runs=0;
35  g_stop=false;
36  DTime dt;
37  dt.set();
38  while(runs++, !g_stop) {
39    cmd();
40  }
41  double delta=dt.udiff()/1000000.0;
42  boost::format fmt("'%s' %.02f seconds: %.1f runs/s, %.02f usec/run");
43
44  cerr<< (fmt % cmd.getName() % delta % (runs/delta) % (delta* 1000000.0/runs)) << endl;
45  g_totalRuns += runs;
46}
47
48struct ARecordTest
49{
50  explicit ARecordTest(int records) : d_records(records) {}
51
52  string getName() const
53  {
54    return (boost::format("%d a records") % d_records).str();
55  }
56
57  void operator()() const
58  {
59    vector<uint8_t> packet;
60    DNSPacketWriter pw(packet, "outpost.ds9a.nl", QType::A);
61    for(int records = 0; records < d_records; records++) {
62      pw.startRecord("outpost.ds9a.nl", QType::A);
63      ARecordContent arc("1.2.3.4");
64      arc.toPacket(pw);
65    }
66    pw.commit();
67  }
68  int d_records;
69};
70
71
72struct MakeStringFromCharStarTest
73{
74  MakeStringFromCharStarTest() : d_size(0){}
75  string getName() const
76  {
77    return (boost::format("make a std::string")).str();
78  }
79
80  void operator()() const
81  {
82    string name("outpost.ds9a.nl");
83    d_size += name.length();
84   
85  }
86  mutable int d_size;
87};
88
89
90struct GetTimeTest
91{
92  string getName() const
93  {
94    return "gettimeofday-test";
95  }
96
97  void operator()() const
98  {
99    struct timeval tv;
100    gettimeofday(&tv, 0);
101  }
102};
103
104
105struct MakeARecordTest
106{
107  string getName() const
108  {
109    return (boost::format("make a-record")).str();
110  }
111
112  void operator()() const
113  {
114      static string src("1.2.3.4");
115      ARecordContent arc(src);
116      //ARecordContent arc(0x01020304);
117
118  }
119};
120
121struct MakeARecordTestMM
122{
123  string getName() const
124  {
125    return (boost::format("make a-record (mm)")).str();
126  }
127
128  void operator()() const
129  {
130      DNSRecordContent*drc = DNSRecordContent::mastermake(QType::A, 1, 
131                                                          "1.2.3.4");
132      delete drc;
133  }
134};
135
136
137struct A2RecordTest
138{
139  explicit A2RecordTest(int records) : d_records(records) {}
140
141  string getName() const
142  {
143    return (boost::format("%d a records") % d_records).str();
144  }
145
146  void operator()() const
147  {
148    vector<uint8_t> packet;
149    DNSPacketWriter pw(packet, "outpost.ds9a.nl", QType::A);
150    ARecordContent arc("1.2.3.4");
151    string name("outpost.ds9a.nl");
152    for(int records = 0; records < d_records; records++) {
153      pw.startRecord(name, QType::A);
154
155      arc.toPacket(pw);
156    }
157    pw.commit();
158  }
159  int d_records;
160};
161
162
163struct TXTRecordTest
164{
165  explicit TXTRecordTest(int records) : d_records(records) {}
166
167  string getName() const
168  {
169    return (boost::format("%d TXT records") % d_records).str();
170  }
171
172  void operator()() const
173  {
174    vector<uint8_t> packet;
175    DNSPacketWriter pw(packet, "outpost.ds9a.nl", QType::TXT);
176    for(int records = 0; records < d_records; records++) {
177      pw.startRecord("outpost.ds9a.nl", QType::TXT);
178      TXTRecordContent arc("\"een leuk verhaaltje in een TXT\"");
179      arc.toPacket(pw);
180    }
181    pw.commit();
182  }
183  int d_records;
184};
185
186
187struct GenericRecordTest
188{
189  explicit GenericRecordTest(int records, uint16_t type, const std::string& content) 
190    : d_records(records), d_type(type), d_content(content) {}
191
192  string getName() const
193  {
194    return (boost::format("%d %s records") % d_records % 
195            DNSRecordContent::NumberToType(d_type)).str();
196  }
197
198  void operator()() const
199  {
200    vector<uint8_t> packet;
201    DNSPacketWriter pw(packet, "outpost.ds9a.nl", d_type);
202    for(int records = 0; records < d_records; records++) {
203      pw.startRecord("outpost.ds9a.nl", d_type);
204      DNSRecordContent*drc = DNSRecordContent::mastermake(d_type, 1, 
205                                                          d_content);
206      drc->toPacket(pw);
207      delete drc;
208    }
209    pw.commit();
210  }
211  int d_records;
212  uint16_t d_type;
213  string d_content;
214};
215
216
217struct AAAARecordTest
218{
219  explicit AAAARecordTest(int records) : d_records(records) {}
220
221  string getName() const
222  {
223    return (boost::format("%d aaaa records (mm)") % d_records).str();
224  }
225
226  void operator()() const
227  {
228    vector<uint8_t> packet;
229    DNSPacketWriter pw(packet, "outpost.ds9a.nl", QType::AAAA);
230    for(int records = 0; records < d_records; records++) {
231      pw.startRecord("outpost.ds9a.nl", QType::AAAA);
232      DNSRecordContent*drc = DNSRecordContent::mastermake(QType::AAAA, 1, "fe80::21d:92ff:fe6d:8441");
233      drc->toPacket(pw);
234      delete drc;
235    }
236    pw.commit();
237  }
238  int d_records;
239};
240
241struct SOARecordTest
242{
243  explicit SOARecordTest(int records) : d_records(records) {}
244
245  string getName() const
246  {
247    return (boost::format("%d soa records (mm)") % d_records).str();
248  }
249
250  void operator()() const
251  {
252    vector<uint8_t> packet;
253    DNSPacketWriter pw(packet, "outpost.ds9a.nl", QType::SOA);
254
255    for(int records = 0; records < d_records; records++) {
256      pw.startRecord("outpost.ds9a.nl", QType::SOA);
257      DNSRecordContent*drc = DNSRecordContent::mastermake(QType::SOA, 1, "a0.org.afilias-nst.info. noc.afilias-nst.info. 2008758137 1800 900 604800 86400");
258      drc->toPacket(pw);
259      delete drc;
260    }
261    pw.commit();
262  }
263  int d_records;
264};
265
266vector<uint8_t> makeEmptyQuery()
267{
268  vector<uint8_t> packet;
269  DNSPacketWriter pw(packet, "outpost.ds9a.nl", QType::SOA);
270  return  packet;
271}
272
273
274vector<uint8_t> makeRootReferral()
275{
276  vector<uint8_t> packet;
277  DNSPacketWriter pw(packet, "outpost.ds9a.nl", QType::SOA);
278
279  // nobody reads what we output, but it appears to be the magic that shuts some nameservers up
280  static const char*ips[]={"198.41.0.4", "192.228.79.201", "192.33.4.12", "128.8.10.90", "192.203.230.10", "192.5.5.241", "192.112.36.4", "128.63.2.53", 
281                     "192.36.148.17","192.58.128.30", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
282  static char templ[40];
283  strncpy(templ,"a.root-servers.net", sizeof(templ) - 1);
284 
285 
286  for(char c='a';c<='m';++c) {
287    *templ=c;
288    pw.startRecord(".", QType::NS, 3600, 1, DNSPacketWriter::AUTHORITY);
289    DNSRecordContent* drc = DNSRecordContent::mastermake(QType::NS, 1, templ);
290    drc->toPacket(pw);
291    delete drc;
292  }
293
294  for(char c='a';c<='m';++c) {
295    *templ=c;
296    pw.startRecord(".", QType::A, 3600, 1, DNSPacketWriter::ADDITIONAL);
297    DNSRecordContent* drc = DNSRecordContent::mastermake(QType::A, 1, ips[c-'a']);
298    drc->toPacket(pw);
299    delete drc;
300  }
301  pw.commit();
302  return  packet;
303
304}
305
306vector<uint8_t> makeTypicalReferral()
307{
308  vector<uint8_t> packet;
309  DNSPacketWriter pw(packet, "outpost.ds9a.nl", QType::A);
310
311  pw.startRecord("ds9a.nl", QType::NS, 3600, 1, DNSPacketWriter::AUTHORITY);
312  DNSRecordContent* drc = DNSRecordContent::mastermake(QType::NS, 1, "ns1.ds9a.nl");
313  drc->toPacket(pw);
314  delete drc;
315
316  pw.startRecord("ds9a.nl", QType::NS, 3600, 1, DNSPacketWriter::AUTHORITY);
317  drc = DNSRecordContent::mastermake(QType::NS, 1, "ns2.ds9a.nl");
318  drc->toPacket(pw);
319  delete drc;
320
321
322  pw.startRecord("ns1.ds9a.nl", QType::A, 3600, 1, DNSPacketWriter::ADDITIONAL);
323  drc = DNSRecordContent::mastermake(QType::A, 1, "1.2.3.4");
324  drc->toPacket(pw);
325  delete drc;
326
327  pw.startRecord("ns2.ds9a.nl", QType::A, 3600, 1, DNSPacketWriter::ADDITIONAL);
328  drc = DNSRecordContent::mastermake(QType::A, 1, "4.3.2.1");
329  drc->toPacket(pw);
330  delete drc;
331
332  pw.commit();
333  return  packet;
334}
335
336
337
338struct RootRefTest
339{
340  string getName() const
341  {
342    return "write rootreferral";
343  }
344
345  void operator()() const
346  {
347    vector<uint8_t> packet=makeRootReferral();
348  }
349
350};
351
352struct StackMallocTest
353{
354  string getName() const
355  {
356    return "stack allocation";
357  }
358
359  void operator()() const
360  {
361    char *buffer= new char[200000];
362    delete buffer;
363  }
364
365};
366
367
368struct EmptyQueryTest
369{
370  string getName() const
371  {
372    return "write empty query";
373  }
374
375  void operator()() const
376  {
377    vector<uint8_t> packet=makeEmptyQuery();
378  }
379
380};
381
382struct TypicalRefTest
383{
384  string getName() const
385  {
386    return "write typical referral";
387  }
388
389  void operator()() const
390  {
391    vector<uint8_t> packet=makeTypicalReferral();
392  }
393
394};
395
396struct TCacheComp
397{
398  bool operator()(const pair<string, QType>& a, const pair<string, QType>& b) const
399  {
400    int cmp=strcasecmp(a.first.c_str(), b.first.c_str());
401    if(cmp < 0)
402      return true;
403    if(cmp > 0)
404      return false;
405
406    return a.second < b.second;
407  }
408};
409
410struct NegCacheEntry
411{
412  string d_name;
413  QType d_qtype;
414  string d_qname;
415  uint32_t d_ttd;
416};
417
418struct timeval d_now;
419
420static bool magicAddrMatch(const QType& query, const QType& answer)
421{
422  if(query.getCode() != QType::ADDR)
423    return false;
424  return answer.getCode() == QType::A || answer.getCode() == QType::AAAA;
425}
426
427
428bool moreSpecificThan(const string& a, const string &b)
429{
430  static string dot(".");
431  int counta=(a!=dot), countb=(b!=dot);
432 
433  for(string::size_type n=0;n<a.size();++n)
434    if(a[n]=='.')
435      counta++;
436  for(string::size_type n=0;n<b.size();++n)
437    if(b[n]=='.')
438      countb++;
439  return counta>countb;
440}
441
442
443struct ParsePacketTest
444{
445  explicit ParsePacketTest(const vector<uint8_t>& packet, const std::string& name) 
446    : d_packet(packet), d_name(name)
447  {}
448
449  string getName() const
450  {
451    return "parse '"+d_name+"'";
452  }
453
454  void operator()() const
455  {
456    MOADNSParser mdp((const char*)&*d_packet.begin(), d_packet.size());
457    typedef map<pair<string, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t;
458    tcache_t tcache;
459   
460    struct {
461            vector<DNSResourceRecord> d_result;
462            bool d_aabit;
463            int d_rcode;
464    } lwr;
465    DNSResourceRecord rr;
466    for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {         
467      DNSResourceRecord rr;
468      rr.qtype=i->first.d_type;
469      rr.qname=i->first.d_label;
470   
471      rr.ttl=i->first.d_ttl;
472      rr.content=i->first.d_content->getZoneRepresentation();  // this should be the serialised form
473      rr.d_place=(DNSResourceRecord::Place) i->first.d_place;
474      lwr.d_result.push_back(rr);
475    }
476
477   
478   
479
480      // reap all answers from this packet that are acceptable
481      for(vector<DNSResourceRecord>::iterator i=lwr.d_result.begin();i != lwr.d_result.end();++i) {
482        if(i->qtype.getCode() == QType::OPT) {
483          // <<prefix<<qname<<": skipping OPT answer '"<<i->qname<<"' from '"<<auth<<"' nameservers" <<endl;
484          continue;
485        }
486        // LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
487        if(i->qtype.getCode()==QType::ANY) {
488          // LOG<<"NO! - we don't accept 'ANY' data"<<endl;
489          continue;
490        }
491        string auth(".");
492        if(dottedEndsOn(i->qname, auth)) {
493          if(lwr.d_aabit && lwr.d_rcode==RCode::NoError && i->d_place==DNSResourceRecord::ANSWER && 0) {
494            // LOG<<"NO! Is from delegation-only zone"<<endl;
495            // s_nodelegated++;
496            return; // RCode::NXDomain;
497          }
498          else {
499            // LOG<<"YES!"<<endl;
500
501          //  i->ttl=min(s_maxcachettl, i->ttl);
502           
503            DNSResourceRecord rr=*i;
504            rr.d_place=DNSResourceRecord::ANSWER;
505
506            // rr.ttl += d_now.tv_sec;
507
508            if(rr.qtype.getCode() == QType::NS) // people fiddle with the case
509              rr.content=toLower(rr.content); // this must stay! (the cache can't be case-insensitive on the RHS of records)
510            tcache[make_pair(i->qname,i->qtype)].insert(rr);
511          }
512        }         
513        else
514          ; // LOG<<"NO!"<<endl;
515      }
516   
517      // supplant
518      for(tcache_t::iterator i=tcache.begin();i!=tcache.end();++i) {
519        if(i->second.size() > 1) {  // need to group the ttl to be the minimum of the RRSET (RFC 2181, 5.2)
520          uint32_t lowestTTL=numeric_limits<uint32_t>::max();
521          for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
522            lowestTTL=min(lowestTTL, j->ttl);
523         
524          for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
525            ((tcache_t::value_type::second_type::value_type*)&(*j))->ttl=lowestTTL;
526        }
527
528        // RC.replace(d_now.tv_sec, i->first.first, i->first.second, i->second, lwr.d_aabit);
529      }
530      set<string, CIStringCompare> nsset; 
531      // LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl;
532
533      bool done=false, realreferral=false, negindic=false;
534      string newauth, soaname, newtarget;
535      string qname(".");
536      vector<DNSResourceRecord> ret;
537      QType qtype(QType::A);
538      string auth(".");
539 
540      for(vector<DNSResourceRecord>::const_iterator i=lwr.d_result.begin();i!=lwr.d_result.end();++i) {
541        if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
542           lwr.d_rcode==RCode::NXDomain) {
543          // LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"'"<<endl;
544          ret.push_back(*i);
545
546          NegCacheEntry ne;
547
548          ne.d_qname=i->qname;
549          ne.d_ttd=d_now.tv_sec + min(i->ttl, 3600U); // controversial
550          ne.d_name=qname;
551          ne.d_qtype=QType(0); // this encodes 'whole record'
552         
553          {
554            // Lock l(&s_negcachelock);
555            // replacing_insert(s_negcache, ne);
556          }
557          negindic=true;
558        }
559        else if(i->d_place==DNSResourceRecord::ANSWER && pdns_iequals(i->qname, qname) && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
560          ret.push_back(*i);
561          newtarget=i->content;
562        }
563        // for ANY answers we *must* have an authoritive answer
564        else if(i->d_place==DNSResourceRecord::ANSWER && pdns_iequals(i->qname, qname) && 
565                (
566                 i->qtype==qtype || (lwr.d_aabit && (qtype==QType(QType::ANY) || magicAddrMatch(qtype, i->qtype) ) )
567                ) 
568               )   
569          {
570         
571          // LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
572
573          done=true;
574          ret.push_back(*i);
575        }
576        else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { 
577          if(moreSpecificThan(i->qname,auth)) {
578            newauth=i->qname;
579            // LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
580            realreferral=true;
581          }
582          else 
583            ;// // LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
584          nsset.insert(i->content);
585        }
586        else if(!done && i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
587           lwr.d_rcode==RCode::NoError) {
588          // LOG<<prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+i->qtype.getName()+"'") <<endl;
589          ret.push_back(*i);
590         
591          NegCacheEntry ne;
592          ne.d_qname=i->qname;
593          ne.d_ttd=d_now.tv_sec + i->ttl;
594          ne.d_name=qname;
595          ne.d_qtype=qtype;
596          if(qtype.getCode()) {  // prevents us from blacking out a whole domain
597           // Lock l(&s_negcachelock);
598            // replacing_insert(s_negcache, ne);
599          }
600          negindic=true;
601        }
602      }
603
604  }
605  const vector<uint8_t>& d_packet;
606  std::string d_name;
607};
608
609struct ParsePacketBareTest
610{
611  explicit ParsePacketBareTest(const vector<uint8_t>& packet, const std::string& name) 
612    : d_packet(packet), d_name(name)
613  {}
614
615  string getName() const
616  {
617    return "parse '"+d_name+"' bare";
618  }
619
620  void operator()() const
621  {
622    MOADNSParser mdp((const char*)&*d_packet.begin(), d_packet.size());
623  }
624  const vector<uint8_t>& d_packet;
625  std::string d_name;
626};
627
628
629struct SimpleCompressTest
630{
631  explicit SimpleCompressTest(const std::string& name) 
632    : d_name(name)
633  {}
634
635  string getName() const
636  {
637    return "compress '"+d_name+"'";
638  }
639
640  void operator()() const
641  {
642    simpleCompress(d_name);
643  }
644  std::string d_name;
645};
646
647struct VectorExpandTest
648{
649  string getName() const
650  {
651    return "vector expand";
652  }
653
654  void operator()() const
655  {
656    vector<uint8_t> d_record;
657    d_record.resize(12);
658
659    string out="\x03www\x04ds9a\x02nl";
660    string::size_type len = d_record.size();
661    d_record.resize(len + out.length());
662    memcpy(&d_record[len], out.c_str(), out.length());
663  }
664
665};
666
667
668
669struct IEqualsTest
670{
671  string getName() const
672  {
673    return "iequals test";
674  }
675
676  void operator()() const
677  {
678      static string a("www.ds9a.nl"), b("www.lwn.net");
679      g_ret = boost::iequals(a, b);
680  }
681
682};
683
684struct MyIEqualsTest
685{
686  string getName() const
687  {
688    return "pdns_iequals test";
689  }
690
691  void operator()() const
692  {
693      static string a("www.ds9a.nl"), b("www.lwn.net");
694      g_ret = pdns_iequals(a, b);
695  }
696
697};
698
699
700struct StrcasecmpTest
701{
702  string getName() const
703  {
704    return "strcasecmp test";
705  }
706
707  void operator()() const
708  {
709      static string a("www.ds9a.nl"), b("www.lwn.net");
710      g_ret = strcasecmp(a.c_str(), b.c_str());
711  }
712};
713
714
715struct NOPTest
716{
717  string getName() const
718  {
719    return "null test";
720  }
721
722  void operator()() const
723  {
724  }
725
726};
727
728
729
730int main(int argc, char** argv)
731try
732{
733  reportAllTypes();
734  doRun(NOPTest());
735
736  doRun(IEqualsTest());
737  doRun(MyIEqualsTest());
738  doRun(StrcasecmpTest());
739
740  doRun(StackMallocTest());
741
742  vector<uint8_t> packet = makeRootReferral();
743  doRun(ParsePacketBareTest(packet, "root-referral"));
744  doRun(ParsePacketTest(packet, "root-referral"));
745
746  doRun(RootRefTest());
747
748  doRun(EmptyQueryTest());
749  doRun(TypicalRefTest());
750
751
752  packet = makeEmptyQuery();
753  doRun(ParsePacketTest(packet, "empty-query"));
754
755  packet = makeTypicalReferral();
756  cerr<<"typical referral size: "<<packet.size()<<endl;
757  doRun(ParsePacketBareTest(packet, "typical-referral"));
758
759  doRun(ParsePacketTest(packet, "typical-referral"));
760
761  doRun(SimpleCompressTest("www.france.ds9a.nl"));
762
763 
764  doRun(VectorExpandTest());
765
766  doRun(GetTimeTest());
767
768  doRun(ARecordTest(1));
769  doRun(ARecordTest(2));
770  doRun(ARecordTest(4));
771  doRun(ARecordTest(64));
772
773  doRun(A2RecordTest(1));
774  doRun(A2RecordTest(2));
775  doRun(A2RecordTest(4));
776  doRun(A2RecordTest(64));
777
778  doRun(MakeStringFromCharStarTest());
779  doRun(MakeARecordTest());
780  doRun(MakeARecordTestMM());
781
782  doRun(AAAARecordTest(1));
783  doRun(AAAARecordTest(2));
784  doRun(AAAARecordTest(4));
785  doRun(AAAARecordTest(64));
786
787  doRun(TXTRecordTest(1));
788  doRun(TXTRecordTest(2));
789  doRun(TXTRecordTest(4));
790  doRun(TXTRecordTest(64));
791
792  doRun(GenericRecordTest(1, QType::NS, "powerdnssec1.ds9a.nl"));
793  doRun(GenericRecordTest(2, QType::NS, "powerdnssec1.ds9a.nl"));
794  doRun(GenericRecordTest(4, QType::NS, "powerdnssec1.ds9a.nl"));
795  doRun(GenericRecordTest(64, QType::NS, "powerdnssec1.ds9a.nl"));
796
797
798
799  doRun(SOARecordTest(1));
800  doRun(SOARecordTest(2));
801  doRun(SOARecordTest(4));
802  doRun(SOARecordTest(64));
803
804  cerr<<"Total runs: " << g_totalRuns<<endl;
805
806}
807catch(std::exception &e)
808{
809  cerr<<"Fatal: "<<e.what()<<endl;
810}
811
Note: See TracBrowser for help on using the browser.