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

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

add gettimeofday speedtest

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