Home | History | Annotate | Download | only in unittest
      1 /*
      2   This file is part of Valgrind, a dynamic binary instrumentation
      3   framework.
      4 
      5   Copyright (C) 2008-2008 Google Inc
      6      opensource (at) 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 (at) 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 
     42 class Mutex64: public Mutex {
     43    // force sizeof(Mutex64) >= 64
     44 private:
     45    char ___[(sizeof(Mutex) > 64) ? (0) : (64 - sizeof(Mutex))];
     46 };
     47 
     48 enum 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 
     60 class 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 
     89 typedef std::map<int, Test> MapOfTests;
     90 MapOfTests the_map_of_tests;
     91 
     92 class GoalStats {
     93 public:
     94    typedef std::vector<StatType> TypesVector;
     95 private:
     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    }
    112 public:
    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 
    204 template <typename RetVal>
    205 struct 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 
    221 ThreadPool * mainThreadPool;
    222 std::map<int, double> map_of_counts; // test -> average run count
    223 
    224 inline double round(double lf) {
    225    return floor(lf + 0.5);
    226 }
    227 
    228 // Accessing memory locations holding one lock {{{1
    229 namespace 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
    317 namespace 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
    393 namespace 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
    570 namespace 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
    604 namespace 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 
    637 typedef std::map<std::string, StatType> StatMap;
    638 StatMap statNames;
    639 int nThreads = 2;
    640 
    641 void 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 
    683 namespace 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 
    697 void 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 
    709 int 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