1/*
2  This file is part of Valgrind, a dynamic binary instrumentation
3  framework.
4
5  Copyright (C) 2008-2008 Google Inc
6     opensource@google.com
7
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of the
11  License, or (at your option) any later version.
12
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  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., 59 Temple Place, Suite 330, Boston, MA
21  02111-1307, USA.
22
23  The GNU General Public License is contained in the file COPYING.
24*/
25
26// Author: Timur Iskhodzhanov <opensource@google.com>
27//
28// This file contains a set of benchmarks for data race detection tools.
29
30#include <vector>
31#include <string>
32#include <map>
33#include <set>
34#include <algorithm>
35#include <cstring>      // strlen(), index(), rindex()
36#include <ctime>
37#include <math.h>
38
39#include "thread_wrappers.h"
40#include "linear_solver.h"
41
42class Mutex64: public Mutex {
43   // force sizeof(Mutex64) >= 64
44private:
45   char ___[(sizeof(Mutex) > 64) ? (0) : (64 - sizeof(Mutex))];
46};
47
48enum StatType {
49   ZZZERO,
50   N_THREADS,
51   N_CV,
52   N_CV_SIGNALS,
53   N_CV_WAITS,
54   N_MUTEXES,
55   N_MUTEX_LOCK_UNLOCK,
56   N_MEM_ACCESSES_K, // kiloaccesses
57   /*N_RACEY_ACCESSES*/
58};
59
60class Test{
61   typedef void (*void_func_void_t)(void);
62
63   /* may return false to indicate smth like "Wait didn't succeed" */
64   typedef bool (*bool_func_void_t)(void);
65   //typedef TestStats (*TestStats_func_void_t)(void);
66
67   //TestStats_func_void_t GetStats_;
68   void_func_void_t Run_v_;
69   bool_func_void_t Run_b_;
70 public:
71   Test() : Run_v_(0), Run_b_(0) {}
72   Test(int id, void_func_void_t _Run) : Run_v_(_Run), Run_b_(0) {
73     CHECK(Run_v_ != NULL);
74   }
75   Test(int id, bool_func_void_t _Run) : Run_v_(0), Run_b_(_Run) {
76     CHECK(Run_b_ != NULL);
77   }
78   bool Run() {
79      if (Run_v_ == NULL) {
80         CHECK(Run_b_ != NULL);
81         return (*Run_b_)();
82      } else {
83         Run_v_();
84         return true;
85      }
86   }
87};
88
89typedef std::map<int, Test> MapOfTests;
90MapOfTests the_map_of_tests;
91
92class GoalStats {
93public:
94   typedef std::vector<StatType> TypesVector;
95private:
96   //TODO: think of better names
97   std::map<StatType, int> goal;
98   TypesVector types;
99   Vector * stats;
100   typedef std::set<void (*)()> void_f_void_set;
101   void_f_void_set param_registerers, param_appliers;
102   std::vector<double*> parameters;
103   Matrix * cost_m;
104   bool calculated;
105
106   template<typename T> static int v_find(const std::vector<T> & vec, const T & val) {
107      for (int i = 0; i < vec.size(); i++)
108         if (vec[i] == val)
109            return i;
110      return -1;
111   }
112public:
113   GoalStats(): stats(NULL), cost_m(NULL), calculated(false) {}
114   ~GoalStats() { delete stats; delete cost_m; }
115
116   // Note that this function is called before main()
117   void AddPattern(void (*register_func)(), void (*paramapply_func)()) {
118      param_registerers.insert(register_func);
119      param_appliers.insert(paramapply_func);
120   }
121
122   void RegisterPatterns() {
123      for(void_f_void_set::iterator i = param_registerers.begin();
124            i != param_registerers.end(); i++)
125      {
126         (*(*i))(); // call each param_registerer
127      }
128   }
129
130   void AddGoal(StatType type, int value) {
131      CHECK(stats == NULL);
132      CHECK(goal.find(type) == goal.end());
133      goal[type] = value;
134      types.push_back(type);
135   }
136
137   void CompileStatsIntoVector() {
138      CHECK(stats == NULL);
139      CHECK(types.size() == goal.size());
140      stats = new Vector(types.size());
141      for (int i = 0; i < types.size(); i++)
142         (*stats)[i] = goal[types[i]];
143      cost_m = new Matrix(types.size(), 0);
144   }
145
146   const Vector & GetStatsVector() {
147      return *stats;
148   }
149
150   TypesVector GetTypes() {
151      CHECK(stats != NULL);
152      return types;
153   }
154
155   void RegisterParameter(double * param) {
156      CHECK(stats != NULL);
157      // int param_id = parameters.size();
158      parameters.push_back(param);
159      cost_m->IncN();
160   }
161
162   void SetParameterStat(StatType stat_type, double * param, double cost) {
163      int type_id = v_find(types, stat_type);
164      if (type_id == -1)
165         return; // this stat type isn't required - ignore
166
167      int param_id = v_find(parameters, param);
168      CHECK(param_id != -1);
169
170      cost_m->At(type_id, param_id) = cost;
171   }
172
173   void CalculateAndApplyParameters() {
174      CHECK(calculated == false);
175      printf("Cost matrix:\n%s\n", cost_m->ToString().c_str());
176      printf("Stats vector:\n%s\n", stats->ToString().c_str());
177      int iterations = 0;
178      Vector params = EstimateParameters(*cost_m, *stats, 0.0005, &iterations);
179      CHECK(params.GetSize() == parameters.size());
180      /*params[0] = 1000;
181      params[1] = 3600;
182      params[2] = 80;
183      params[3] = 0;
184      params[4] = 19530;
185      params[5] = 1720;*/
186      printf("Parameters (estimated in %d steps) :\n", iterations);
187      for (int i = 0; i < parameters.size(); i++) {
188         printf("param[%i] = %lf\n", i, params[i]);
189         *(parameters[i]) = params[i];
190      }
191      printf("Est. stats: %s\n", cost_m->MultiplyRight(params).ToString().c_str());
192
193      for (void_f_void_set::iterator i = param_appliers.begin();
194               i != param_appliers.end(); i++)
195      {
196         (*(*i))();
197      }
198      fflush(stdout);
199
200      calculated = true;
201   }
202} goals;
203
204template <typename RetVal>
205struct TestAdder {
206   TestAdder(int id, RetVal (*_Run)(),
207                     void (*paramreg)(void),
208                     void (*paramapply)(void))
209   {
210      CHECK(the_map_of_tests.count(id) == 0);
211      the_map_of_tests[id] = Test(id, _Run);
212      goals.AddPattern(paramreg, paramapply);
213   }
214};
215
216#define REGISTER_PATTERN(id) TestAdder<void> add_test##id(id, Pattern##id, \
217                             ParametersRegistration##id, ApplyParameters##id)
218#define REGISTER_PATTERN_PROB(id) TestAdder<bool> add_test##id(id, Pattern##id, \
219                             ParametersRegistration##id, ApplyParameters##id)
220
221ThreadPool * mainThreadPool;
222std::map<int, double> map_of_counts; // test -> average run count
223
224inline double round(double lf) {
225   return floor(lf + 0.5);
226}
227
228// Accessing memory locations holding one lock {{{1
229namespace one_lock {
230   struct Params {
231      double num_contexts;
232      int NUM_CONTEXTS;
233
234      double num_iterations_times_runcount;
235      int NUM_ITERATIONS;
236
237      //double data_size_times_;
238      static const int DATA_SIZE = 128;
239      static const int REDO_CNT  = 2;
240   } params;
241
242   struct TestContext {
243      Mutex64 MU;
244      int * data;
245      TestContext() {
246         data = new int[params.DATA_SIZE];
247      }
248   } *contexts;
249
250   // Write accesses
251   void Pattern101() {
252      int id = rand() % params.NUM_CONTEXTS;
253      TestContext * context = &contexts[id];
254      for (int i = 0; i < params.NUM_ITERATIONS; i++) {
255         context->MU.Lock();
256            for (int j = 0; j < params.DATA_SIZE; j++) {
257               for (int k = 0; k < params.REDO_CNT; k++)
258                  context->data[j] = 77; // write
259            }
260         context->MU.Unlock();
261      }
262   }
263   void ParametersRegistration101() {
264      map_of_counts[101] = 100;
265      //goals.RegisterParameter(&map_of_counts[101]);
266
267      goals.RegisterParameter(&params.num_iterations_times_runcount);
268      goals.SetParameterStat(N_MUTEX_LOCK_UNLOCK, &params.num_iterations_times_runcount, 3 /*don't ask why*/);
269      goals.SetParameterStat(N_MEM_ACCESSES_K, &params.num_iterations_times_runcount,
270                             params.DATA_SIZE * params.REDO_CNT * 2.0 / 1000.0);
271
272      goals.RegisterParameter(&params.num_contexts);
273      goals.SetParameterStat(N_MUTEXES, &params.num_contexts, 1);
274   }
275   void ApplyParameters101() {
276      if (map_of_counts[101] < 1.0)
277         map_of_counts[101] = 1.0;
278      params.NUM_CONTEXTS = round(params.num_contexts);
279      if (params.NUM_CONTEXTS <= 0)
280         params.NUM_CONTEXTS = 1;
281      params.NUM_ITERATIONS = round(params.num_iterations_times_runcount / map_of_counts[101]);
282
283      contexts = new TestContext[params.NUM_CONTEXTS];
284   }
285   REGISTER_PATTERN(101);
286
287   /* other tests...
288   // Read accesses
289   void Pattern102() {
290      int id = rand() % NUM_CONTEXTS;
291      TestContext * context = &contexts[id];
292      for (int i = 0; i < NUM_ITERATIONS; i++) {
293         int temp = 0;
294         context->MU.Lock();
295            for (int j = 0; j < DATA_SIZE; j++) {
296               for (int k = 0; k < 10; k++)
297                  temp += context->data[j]; // read
298            }
299         context->MU.Unlock();
300      }
301   }
302   REGISTER_PATTERN(102);
303
304
305   int atomic_integers[NUM_CONTEXTS] = {0};
306   // Atomic increment
307   void Pattern103() {
308      int id = rand() % NUM_CONTEXTS;
309      for (int i = 0; i < NUM_ITERATIONS; i++)
310         __sync_add_and_fetch(&atomic_integers[id], 1);
311   }
312   REGISTER_PATTERN(103);
313   */
314} // namespace one_lock
315/* other namespaces...
316// Accessing memory locations holding random LockSets {{{1
317namespace multiple_locks {
318   // TODO: make these constants as parameters
319   const int NUM_CONTEXTS   = 1024;
320   const int DATA_SIZE      = 4096;
321   const int NUM_ITERATIONS = 1;
322   const int LOCKSET_SIZE   = 2;
323
324   struct TestContext {
325      Mutex64 MU;
326      int data[DATA_SIZE];
327   } contexts[NUM_CONTEXTS];
328
329   // Access random context holding a random LS including context->MU
330   void Pattern201() {
331      TestContext * context = &contexts[rand() % NUM_CONTEXTS];
332      std::vector<Mutex64*> LS;
333      // STL nightmare starts here - calculate random LS{{{1
334      {
335         std::vector<int> tmp_LS;
336         for (int i = 0; i < NUM_CONTEXTS; i++)
337            tmp_LS.push_back(i);
338         std::random_shuffle(tmp_LS.begin(), tmp_LS.end());
339
340         // TODO: #LS as a parameter
341         for (int i = 0; i < LOCKSET_SIZE; i++)
342            LS.push_back(&contexts[tmp_LS[i]].MU);
343
344         // This LS should contain context's Mutex to have proper synchronization
345         LS.push_back(&context->MU);
346
347         // LS should be sorted to avoid deadlocks
348         std::sort(LS.begin(), LS.end());
349
350         // LS should not contain context->MU twice
351         std::vector<Mutex64*>::iterator new_end = std::unique(LS.begin(), LS.end());
352         LS.erase(new_end, LS.end());
353      } // end of STL nightmare :-)
354
355      for (int i = 0; i < NUM_ITERATIONS; i++) {
356         for (std::vector<Mutex64*>::iterator it = LS.begin(); it != LS.end(); it++)
357            (*it)->Lock();
358         for (int j = 0; j < DATA_SIZE; j++)
359            context->data[j] = 77;
360         for (std::vector<Mutex64*>::reverse_iterator it = LS.rbegin(); it != LS.rend(); it++)
361            (*it)->Unlock();
362      }
363   }
364   REGISTER_PATTERN(201);
365
366   const int MAX_LOCKSET_SIZE   = 3;
367   const int NUM_LOCKSETS = 1 << MAX_LOCKSET_SIZE;
368   Mutex64 ls_mu[MAX_LOCKSET_SIZE];
369   char ls_data[NUM_LOCKSETS][DATA_SIZE];
370   // Access random context holding a corresponding LockSet
371   void Pattern202() {
372      int ls_idx = 0;
373      while (ls_idx == 0)
374         ls_idx = rand() % NUM_LOCKSETS;
375
376      char * data = ls_data[ls_idx];
377      for (int i = 0; i < MAX_LOCKSET_SIZE; i++)
378         if (ls_idx & (1 << i))
379            ls_mu[i].Lock();
380
381      for (int j = 0; j < DATA_SIZE; j++)
382         data[j] = 77;
383
384      for (int i = MAX_LOCKSET_SIZE - 1; i >= 0; i--)
385         if (ls_idx & (1 << i))
386            ls_mu[i].Unlock();
387   }
388   REGISTER_PATTERN(202);
389} // namespace multiple_locks
390*/
391
392// Publishing objects using different synchronization patterns {{{1
393namespace publishing {
394   /*namespace pcq {
395      const int NUM_CONTEXTS = 16;
396
397      struct Params {
398        double num_contexts;
399        int NUM_CONTEXTS;
400
401        double data_size_times_runcount;
402        int DATA_SIZE;
403      } params;
404
405      struct TestContext {
406         ProducerConsumerQueue pcq;
407
408         TestContext() : pcq(0) {}
409         ~TestContext() {
410            void * ptr = NULL;
411            // Erase the contents of the PCQ. We assume NULL can't be there
412            pcq.Put(NULL);
413            while(ptr = pcq.Get())
414               free(ptr);
415         }
416      } * contexts;
417
418      // Publish a random string into a random PCQ
419      void Pattern301() {
420         TestContext * context = &contexts[rand() % params.NUM_CONTEXTS];
421         // TODO: str_len as a parameter
422         int str_len = 1 + (rand() % params.DATA_SIZE);
423         char * str = (char*)malloc(str_len + 1);
424         CHECK(str != NULL);
425         memset(str, 'a', str_len);
426         str[str_len] = '\0';
427         context->pcq.Put(str);
428      }
429      REGISTER_PATTERN(301);
430
431      // Read a published string from a random PCQ. MAYFAIL!
432      bool Pattern302() {
433         TestContext * context = &contexts[rand() % NUM_CONTEXTS];
434         char * str = NULL;
435         if (context->pcq.TryGet((void**)&str)) {
436            int tmp = strlen(str);
437            free(str);
438            return true;
439         }
440         return false;
441      }
442      REGISTER_PATTERN(302);
443   }*/
444
445   namespace condvar {
446      struct Params {
447         double num_contexts;
448         int NUM_CONTEXTS;
449
450         double data_size_times_runcount;
451         int DATA_SIZE;
452         Params() {
453            DATA_SIZE = 1;
454            HIT_PROBABILITY = 0.3; // estimate. TODO: think of a better idea
455         }
456
457         const static int REDO = 100;
458         double HIT_PROBABILITY;
459
460         double EstimateRuncount() {
461            return map_of_counts[311] + HIT_PROBABILITY * map_of_counts[312];
462         }
463      } params;
464
465      struct TestContext {
466         Mutex64 MU;
467         CondVar CV;
468         int CV_Signalled;
469
470         char * data;
471         TestContext () {
472            data = NULL;
473            CV_Signalled = 0;
474         }
475      } *contexts;
476
477      // Signal a random CV
478      void Pattern311() {
479         int id = rand() % params.NUM_CONTEXTS;
480         TestContext * context = &contexts[id];
481         context->MU.Lock();
482         if (context->data) {
483            free(context->data);
484         }
485         int LEN = params.DATA_SIZE;
486         context->data = (char*)malloc(LEN + 1);
487         for (int i = 0; i < params.REDO; i++)
488            for (int j = 0; j < LEN; j++)
489               context->data[j] = 'a';
490         context->data[LEN] = '\0';
491         context->CV.Signal();
492         context->CV_Signalled = params.NUM_CONTEXTS;
493         context->MU.Unlock();
494      }
495      void ParametersRegistration311() {
496         double * num_CV_Signals = &map_of_counts[311];
497         goals.RegisterParameter(num_CV_Signals);
498         goals.SetParameterStat(N_CV_SIGNALS, num_CV_Signals, 1);
499         goals.SetParameterStat(N_MUTEX_LOCK_UNLOCK, num_CV_Signals, 9 /* don't ask why */);
500
501         goals.RegisterParameter(&params.num_contexts);
502         goals.SetParameterStat(N_CV, &params.num_contexts, 1);
503         goals.SetParameterStat(N_MUTEXES, &params.num_contexts, 1);
504
505         goals.RegisterParameter(&params.data_size_times_runcount);
506         goals.SetParameterStat(N_MEM_ACCESSES_K, &params.data_size_times_runcount,
507                                  (2*params.REDO) * (1.0 + params.HIT_PROBABILITY) / 1000.0);
508      }
509      void ApplyParameters311() {
510        if (map_of_counts[311] < 1.0)
511          map_of_counts[311] = 1.0;
512
513        params.DATA_SIZE = 1 + round(params.data_size_times_runcount / params.EstimateRuncount());
514        /*if (params.DATA_SIZE < 1)
515          params.DATA_SIZE = 1;*/
516
517        params.NUM_CONTEXTS = round(params.num_contexts);
518        if (params.NUM_CONTEXTS < 1)
519          params.NUM_CONTEXTS = 1;
520        contexts = new TestContext[params.NUM_CONTEXTS];
521      }
522      /*void TMP311 ()  {
523        map_of_counts[311]  = 1000;
524        params.NUM_CONTEXTS = 100;
525        params.DATA_SIZE    = 10000;
526        contexts = new TestContext[params.NUM_CONTEXTS];
527      }*/
528      REGISTER_PATTERN(311);
529
530      // Wait on a random CV
531      bool Pattern312() {
532         int nAttempts = 0,
533             id = rand() % params.NUM_CONTEXTS;
534         TestContext * context;
535
536         do {
537            if (nAttempts++ > params.NUM_CONTEXTS)
538               return false;
539            context = &contexts[id];
540            id = (id + 1) % params.NUM_CONTEXTS;
541         } while (ANNOTATE_UNPROTECTED_READ(context->CV_Signalled) == 0);
542
543         context->MU.Lock();
544         context->CV_Signalled--;
545         bool ret = !context->CV.WaitWithTimeout(&context->MU, 10);
546         if (ret && context->data) {
547            // int tmp = strlen(context->data);
548            free(context->data);
549            context->data = NULL;
550         }
551         context->MU.Unlock();
552         return ret;
553      }
554      void ParametersRegistration312() {
555         double * num_CV_Waits = &map_of_counts[312];
556         goals.RegisterParameter(num_CV_Waits);
557         goals.SetParameterStat(N_CV_WAITS, num_CV_Waits, params.HIT_PROBABILITY);
558         goals.SetParameterStat(N_MUTEX_LOCK_UNLOCK, num_CV_Waits, 16);
559         // N_MEM_ACCESSES_K is counted in ParametersRegistration312
560      }
561      void ApplyParameters312() {
562         //nothing to do, see ApplyParameters311
563      }
564      REGISTER_PATTERN_PROB(312);
565   }
566} // namespace publishing
567
568/*
569// Threads work with their memory exclusively {{{1
570namespace thread_local {
571   // Thread accesses heap
572   void Pattern401() {
573      // TODO: parameters
574      const int DATA_SIZE  = 1024;
575      const int ITERATIONS = 16;
576
577      char * temp = (char*)malloc(DATA_SIZE + 1);
578      for (int i = 1; i <= ITERATIONS; i++) {
579         memset(temp, i, DATA_SIZE);
580         temp[DATA_SIZE] = 0;
581         int size = strlen(temp);
582      }
583      free(temp);
584   }
585   REGISTER_PATTERN(401);
586
587   // Thread accesses stack
588   void Pattern402() {
589      // TODO: parameters
590      const int DATA_SIZE  = 1024;
591      const int ITERATIONS = 16;
592
593      char temp[DATA_SIZE];
594      for (int i = 1; i <= ITERATIONS; i++) {
595         memset(temp, i, DATA_SIZE);
596         temp[DATA_SIZE] = 0;
597         int size = strlen(temp);
598      }
599   }
600   REGISTER_PATTERN(402);
601} // namespace thread_local
602
603// Different benign races scenarios {{{1
604namespace benign_races {
605   namespace stats {
606      int simple_counter = 0;
607
608      int odd_counter = 1;
609      Mutex64 odd_counter_mu;
610
611      struct __ {
612         __() {
613            ANNOTATE_BENIGN_RACE(&simple_counter, "Pattern501");
614         }
615      } _;
616
617      void Pattern501() {
618         simple_counter++;
619      }
620      REGISTER_PATTERN(501);
621
622      // increment odd_counter, but first check it is >0 (double-check)
623      void Pattern502() {
624         if (ANNOTATE_UNPROTECTED_READ(odd_counter) > 0) {
625            odd_counter_mu.Lock();
626            if (odd_counter > 0)
627               odd_counter++;
628            odd_counter_mu.Unlock();
629         }
630      }
631      REGISTER_PATTERN(502);
632   }
633
634} // namespace benign_races
635*/
636
637typedef std::map<std::string, StatType> StatMap;
638StatMap statNames;
639int nThreads = 2;
640
641void PatternDispatcher() {
642   /*std::vector<int> availablePatterns;
643   for (MapOfTests::iterator it = the_map_of_tests.begin();
644         it != the_map_of_tests.end(); it++) {
645      if (map_of_counts[it->first] > 0.0)
646         availablePatterns.push_back(it->first);
647   }*/
648
649   std::map<int, int> this_thread_runcounts;
650
651   int total = 0;
652   for (std::map<int,double>::iterator it = map_of_counts.begin(); it != map_of_counts.end(); it++) {
653      CHECK(it->second >= 0.0);
654      int count = round(it->second / nThreads);
655      this_thread_runcounts[it->first] = count;
656      total += count;
657   }
658
659   CHECK(total > 0);
660
661   for (int i = 0; i < total; i++) {
662   //while (total > 0) {
663      int rnd = rand() % total;
664      int test_idx = -1;
665      for (std::map<int,int>::iterator it = this_thread_runcounts.begin(); it != this_thread_runcounts.end(); it++) {
666         int this_test_count = it->second;
667         if (rnd < this_test_count) {
668            test_idx = it->first;
669            break;
670         }
671         rnd -= this_test_count;
672      }
673      CHECK(test_idx >= 0);
674      // TODO: the above code should be replaced with a proper randomizer
675      // with a "specify distribution function" feature
676      if (the_map_of_tests[test_idx].Run()) {
677      /*   this_thread_runcounts[test_idx]--;
678         total--;*/
679      }
680   }
681}
682
683namespace N_THREADS_hack {
684  double nThreads_double = 2.0;
685
686  void Registerer() {
687    goals.RegisterParameter(&nThreads_double);
688    goals.SetParameterStat(N_THREADS, &nThreads_double, 1);
689  }
690
691  void Applier() {
692    nThreads = round(nThreads_double);
693    CHECK(nThreads >= 2);
694  }
695}
696
697void RegisterStatNames() {
698#define REGISTER_STAT_NAME(a) statNames[#a] = a
699  goals.AddPattern(N_THREADS_hack::Registerer, N_THREADS_hack::Applier);
700  REGISTER_STAT_NAME(N_THREADS);
701  REGISTER_STAT_NAME(N_CV);
702  REGISTER_STAT_NAME(N_CV_SIGNALS);
703  REGISTER_STAT_NAME(N_CV_WAITS);
704  REGISTER_STAT_NAME(N_MUTEXES);
705  REGISTER_STAT_NAME(N_MUTEX_LOCK_UNLOCK);
706  REGISTER_STAT_NAME(N_MEM_ACCESSES_K);
707}
708
709int main(int argc, const char **argv) {
710   long init = GetTimeInMs();
711   RegisterStatNames();
712   const char *default_goals[] = {"N_THREADS=20", "N_MEM_ACCESSES_K=130000",
713        "N_MUTEXES=1800", "N_CV=80", "N_MUTEX_LOCK_UNLOCK=107000",
714        "N_CV_SIGNALS=3600", "N_CV_WAITS=500"};
715   const char ** goal_list = NULL;
716   int goal_cnt = 0;
717   if (argc == 1) {
718      printf("Running the default pattern\n");
719      goal_list = default_goals;
720      goal_cnt  = sizeof(default_goals) / sizeof(*default_goals);
721   } else if (argc == 2 && !strcmp(argv[1], "--help")) {
722      printf("Usage: bigtest [PARAM=VALUE] ...\n  Available params: ");
723      for (StatMap::iterator i = statNames.begin(); i != statNames.end(); i++) {
724         printf ("%s%s", (i == statNames.begin()) ? "" : ", ",
725                         (*i).first.c_str());
726      }
727      printf("\n");
728      return 0;
729   } else {
730     goal_list = argv + 1;
731     goal_cnt  = argc - 1;
732   }
733
734   {
735      // Parse goal strings
736      for (int i = 0; i < goal_cnt; i++) {
737         const char * goal = goal_list[i];
738         char stat[256] = "";
739         int stat_val = -1, j = 0;
740         for (; j < sizeof(stat) - 1
741                  && goal[j] != '='
742                  && goal[j] != '\0'; j++) {
743            stat[j] = goal[j];
744         }
745         stat[j] = '\0';
746         if (goal[j] == '=')
747             sscanf(goal + j + 1, "%i", &stat_val);
748         printf("%s = %i\n", stat, stat_val);
749         if (goal[j] != '='
750             || strlen(stat) == 0
751             || stat_val < 0
752             || statNames.find(stat) == statNames.end()
753             ) {
754            fprintf(stderr, "Error parsing goal \"%s\"\n", goal);
755            CHECK(0);
756         }
757         goals.AddGoal(statNames[stat], stat_val);
758      }
759      printf("\n");
760   }
761   goals.CompileStatsIntoVector();
762   Vector statsVector = goals.GetStatsVector();
763   goals.RegisterPatterns();
764   goals.CalculateAndApplyParameters();/**/
765   long start = GetTimeInMs();
766   printf("\nParameters calculated in %dms\nBenchmarking...\n",
767          (int)(start - init));
768   // Start (N_THREADS - 1) new threads...
769   mainThreadPool = new ThreadPool(nThreads - 1);
770   mainThreadPool->StartWorkers();
771   for (int i = 0; i < nThreads - 1; i++) {
772      mainThreadPool->Add(NewCallback(PatternDispatcher));
773   }
774   PatternDispatcher(); // and 1 more in the main thread
775   delete mainThreadPool;
776   long end = GetTimeInMs();
777   printf("...done in %dms\n", (int)(end - start));
778   printf("*RESULT bigtest: time= %d ms\n", (int)(end - start));
779   return 0;
780}
781