485f95c1e6f2155c84b6e045cf067652d391d861
[libcds.git] / tests / cppunit / test_main.cpp
1 //$$CDS-header$$
2
3 /*
4  * Copyright (c) 2003, 2004
5  * Zdenek Nemec
6  *
7  * This material is provided "as is", with absolutely no warranty expressed
8  * or implied. Any use is at your own risk.
9  *
10  * Permission to use or copy this software for any purpose is hereby granted
11  * without fee, provided the above notices are retained on all copies.
12  * Permission to modify the code and to distribute modified code is granted,
13  * provided the above notices are retained, and a notice that the code was
14  * modified is included with the above copyright notice.
15  *
16  */
17
18 /*
19 #if defined(_DEBUG) && _MSC_VER == 1500
20 #    define _CRTDBG_MAP_ALLOC
21 #    include <stdlib.h>
22 #    include <crtdbg.h>
23 #endif
24 */
25
26 #include "cppunit/cppunit_proxy.h"
27 #include "cppunit/file_reporter.h"
28
29 #include <cds/init.h>
30 #include <cds/gc/hp.h>
31 #include <cds/gc/dhp.h>
32 #include <cds/urcu/general_instant.h>
33 #include <cds/urcu/general_buffered.h>
34 #include <cds/urcu/general_threaded.h>
35 #include <cds/urcu/signal_buffered.h>
36 #include <cds/urcu/signal_threaded.h>
37 #include <cds/os/topology.h>
38
39 #include "stdio.h"
40 #include <fstream>
41 #include <iostream>
42 #include <set>
43 #include <boost/date_time/posix_time/posix_time.hpp>
44 #include <boost/thread/mutex.hpp>
45
46 // Visual leak detector (see http://vld.codeplex.com/)
47 #if defined(CDS_USE_VLD) && CDS_COMPILER == CDS_COMPILER_MSVC
48 #   ifdef _DEBUG
49 #       include <vld.h>
50 #   endif
51 #endif
52
53
54 std::ostream& operator << (std::ostream& s, const cds::gc::hp::GarbageCollector::InternalState& stat)
55 {
56     s << "\nHZP GC internal state:"
57         << "\n\t\tHP record allocated=" << stat.nHPRecAllocated
58         << "\n\t\tHP records used=" << stat.nHPRecUsed
59         << "\n\t\tTotal retired ptr count=" << stat.nTotalRetiredPtrCount
60         << "\n\t\tRetired ptr in free HP records=" << stat.nRetiredPtrInFreeHPRecs
61         << "\n\tEvents:"
62         << "\n\t\tHPRec allocations=" << stat.evcAllocHPRec
63         << "\n\t\tHPRec retire events=" << stat.evcRetireHPRec
64         << "\n\t\tnew HPRec allocations from heap=" << stat.evcAllocNewHPRec
65         << "\n\t\tHPRec deletions=" << stat.evcDeleteHPRec
66         << "\n\t\tScan calling=" << stat.evcScanCall
67         << "\n\t\tHelpScan calling=" << stat.evcHelpScanCall
68         << "\n\t\tScan calls from HelpScan=" << stat.evcScanFromHelpScan
69         << "\n\t\tretired objects deleting=" << stat.evcDeletedNode
70         << "\n\t\tguarded objects on Scan=" << stat.evcDeferredNode
71         << std::endl;
72
73     return s;
74 }
75
76 namespace CppUnitMini
77 {
78   int TestCase::m_numErrors = 0;
79   int TestCase::m_numTests = 0;
80   std::vector<std::string>  TestCase::m_arrStrings;
81   bool TestCase::m_bPrintGCState = false;
82   std::string TestCase::m_strTestDataDir(".");
83   Config TestCase::m_Cfg;
84
85   TestCase * TestCase::m_pCurTestCase = nullptr;
86
87   TestCase *TestCase::m_root = 0;
88   Reporter *TestCase::m_reporter = 0;
89
90   void TestCase::registerTestCase(TestCase *in_testCase) {
91     in_testCase->m_next = m_root;
92     m_root = in_testCase;
93   }
94
95   int TestCase::run(Reporter *in_reporter, const char *in_testName, bool invert)
96   {
97     TestCase::m_reporter = in_reporter;
98
99     m_numErrors = 0;
100     m_numTests = 0;
101
102     TestCase *tmp = m_root;
103     while (tmp != 0) {
104       m_pCurTestCase = tmp;
105       try {
106         tmp->myRun(in_testName, invert);
107       } catch ( std::exception& ex ) {
108         in_reporter->message( "EXCEPTION: ");
109         in_reporter->message( ex.what() );
110       }
111       tmp = tmp->m_next;
112     }
113
114     return m_numErrors;
115   }
116
117   void TestCase::print_gc_state()
118   {
119       if ( m_bPrintGCState ) {
120           {
121               cds::gc::hp::GarbageCollector::InternalState stat;
122               std::cout << cds::gc::hp::GarbageCollector::instance().getInternalState( stat ) << std::endl;
123           }
124       }
125   }
126
127   void Config::load( const char * fileName )
128   {
129       std::ifstream s;
130       s.open( fileName );
131       if ( !s.is_open() ) {
132           std::cerr << "WARNING: Cannot open test cfg file " << fileName
133               << "\n\tUse default settings"
134               << std::endl;
135           return;
136       }
137
138       std::cout << "Using test config file: " << fileName << std::endl;
139
140       char buf[ 4096 ];
141
142       TestCfg * pMap = nullptr;
143       while ( !s.eof() ) {
144           s.getline( buf, sizeof(buf)/sizeof(buf[0]) );
145           char * pszStr = buf;
146           // trim left
147           while ( *pszStr != 0 && (*pszStr == ' ' || *pszStr == '\t' )) ++pszStr;
148           // trim right
149           char * pszEnd = strchr( pszStr, 0 );
150           if ( pszEnd == pszStr )    // empty srtring
151               continue;
152           --pszEnd;
153           while ( pszEnd != pszStr && (*pszEnd ==' ' || *pszEnd=='\t' || *pszEnd=='\n' || *pszEnd=='\r' )) --pszEnd;
154
155           if ( pszStr == pszEnd  )    // empty string
156               continue;
157
158           pszEnd[1] = 0;
159
160           if ( *pszStr == '#' )    // comment
161               continue;
162
163           if ( *pszStr == '[' && *pszEnd == ']' ) {    // chapter header
164               *pszEnd = 0;
165               pMap = &( m_Cfg[ pszStr + 1 ] );
166               continue;
167           }
168
169           if ( !pMap )
170               continue;
171
172           char * pszEq = strchr( pszStr, '=' );
173           if ( !pszEq )
174               continue;
175           if ( pszEq == pszStr )
176               continue;
177
178           pszEnd = pszEq;
179           while ( pszStr <= --pszEnd && (*pszEnd ==' ' || *pszEnd=='\t' || *pszEnd=='\n' || *pszEnd=='\r') );
180
181           if ( pszEnd <= pszStr )
182               continue;
183           pszEnd[1] = 0;
184           pMap->m_Cfg[ pszStr ] = pszEq + 1;
185       }
186       s.close();
187   }
188
189   std::vector<std::string> const &    TestCase::getTestStrings()
190   {
191       if ( m_arrStrings.empty() ) {
192           std::string strTestDir = m_strTestDataDir;
193
194           std::ifstream fDict;
195           char bufLine[1024];
196           std::cout << "Loading test data " << strTestDir << "/dictionary.txt..." << std::endl;
197           fDict.open( (strTestDir + "/dictionary.txt").c_str() );
198           if ( fDict.is_open() ) {
199               cds::OS::Timer timer;
200               std::string str;
201               fDict >> str    ;   // number of lines in file
202
203               // Assume that dictionary.txt does not contain doubles.
204               CppUnitMini::TestCase::m_arrStrings.reserve( atol(str.c_str()) );
205               while ( !fDict.eof() ) {
206                   fDict.getline( bufLine, sizeof(bufLine)/sizeof(bufLine[0]) );
207                   if ( bufLine[0] )
208                     m_arrStrings.push_back( bufLine );
209               }
210               fDict.close();
211
212               std::cout << "  Duration=" << timer.duration() << " String count " << CppUnitMini::TestCase::m_arrStrings.size() << std::endl;
213           }
214           else
215               std::cout << "  Failed, file not found" << std::endl;
216
217       }
218       return m_arrStrings;
219   }
220 }
221
222 static void usage(const char* name)
223 {
224   printf("Usage : %s [-t=<class>[::<test>]] [-x=<class>[::<test>]] [-f=<file>] [-m]\n", name);
225   printf("\t[-t=<class>[::<test>]] : test class or class::test to execute;\n");
226   printf("\t[-x=<class>[::<test>]] : test class or class::test to exclude from execution;\n");
227   printf("\t[-d=dir] : test data directory (default is .);\n");
228   printf("\t[-f=<file>] : output file");
229   //printf(";\n\t[-m] : monitor test execution, display time duration for each test\n");
230   printf("\t[-gc_state] : print gc state after each test\n");
231   printf("\t[-cfg=<file>] : config file name for tests\n");
232 }
233
234 int main(int argc, char** argv)
235 {
236
237 #ifdef CDS_MSVC_MEMORY_LEAKS_DETECTING_ENABLED
238     _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
239 #endif
240
241   // CppUnit(mini) test launcher
242   // command line option syntax:
243   // test [Options]
244   // where Options are
245   //  -t=CLASS[::TEST]    run the test class CLASS or member test CLASS::TEST
246   //  -x=CLASS[::TEST]    run all except the test class CLASS or member test CLASS::TEST
247   //  -d=dir              test data directory (default is .)
248   //  -f=FILE             save output in file FILE instead of stdout
249   //  -m                  monitor test(s) execution
250   //  -gc_state           print GC state after test
251   const char *fileName = 0;
252   const char *testName = "";
253   const char *xtestName = "";
254   const char *testDataDir = ".";
255   const char *cfgFileName =
256 #ifdef _DEBUG
257       "test-debug.conf"
258 #else
259       "test.conf"
260 #endif
261 ;
262   bool doMonitoring = false;
263
264   for (int i = 1; i < argc; ++i) {
265     if (argv[i][0] == '-') {
266       if (!strncmp(argv[i], "-t=", 3)) {
267         testName = argv[i]+3;
268         continue;
269       }
270       else if (!strncmp(argv[i], "-f=", 3)) {
271         fileName = argv[i]+3;
272         continue;
273       }
274       else if (!strncmp(argv[i], "-x=", 3)) {
275         xtestName = argv[i]+3;
276         continue;
277       }
278       else if (!strncmp(argv[i], "-d=", 3)) {
279           testDataDir = argv[i] + 3;
280           continue;
281       }
282       else if ( !strncmp(argv[i], "-m", 2)) {
283         doMonitoring = true;
284         continue;
285       }
286       else if (!strncmp(argv[i], "-gc_state", 9)) {
287           CppUnitMini::TestCase::m_bPrintGCState = true;
288           continue;
289       }
290       else if( !strncmp(argv[i], "-cfg=", 5)) {
291           cfgFileName = argv[i] + 5;
292           continue;
293       }
294     }
295
296     // invalid option, we display normal usage.
297     usage(argv[0]);
298     return 1;
299
300   }
301
302   {
303     boost::posix_time::ptime cur( boost::posix_time::second_clock::local_time());
304
305     std::cout << "libcds version " << CDS_VERSION_STRING << "\n";
306     std::cout << "Test started " << cur << std::endl;
307   }
308
309
310   CppUnitMini::TestCase::m_strTestDataDir = testDataDir;
311
312   CppUnitMini::Reporter* reporter;
313   if (fileName != 0)
314     reporter = new CppUnitMini::FileReporter(fileName, doMonitoring);
315   else
316     reporter = new CppUnitMini::FileReporter(stdout, doMonitoring);
317
318   // Load config params
319   CppUnitMini::TestCase::m_Cfg.load( cfgFileName );
320
321   // Init CDS runtime
322   cds::Initialize();
323
324   int num_errors;
325   {
326       size_t nHazardPtrCount = 0;
327       {
328         CppUnitMini::TestCfg& cfg = CppUnitMini::TestCase::m_Cfg.get( "General" );
329         nHazardPtrCount = cfg.getULong( "hazard_pointer_count", 0 );
330       }
331
332       // Safe reclamation schemes
333       cds::gc::HP hzpGC( nHazardPtrCount );
334       cds::gc::PTB ptbGC;
335
336       // RCU varieties
337       typedef cds::urcu::gc< cds::urcu::general_instant<> >    rcu_gpi;
338       rcu_gpi   gpiRCU;
339
340       typedef cds::urcu::gc< cds::urcu::general_buffered<> >    rcu_gpb;
341       rcu_gpb   gpbRCU;
342
343       typedef cds::urcu::gc< cds::urcu::general_threaded<> >    rcu_gpt;
344       rcu_gpt   gptRCU;
345
346 #ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
347       typedef cds::urcu::gc< cds::urcu::signal_buffered<> >    rcu_shb;
348       rcu_shb   shbRCU( 256, SIGUSR1 );
349
350       typedef cds::urcu::gc< cds::urcu::signal_threaded<> >    rcu_sht;
351       rcu_sht   shtRCU( 256, SIGUSR2 );
352 #endif
353
354       // System topology
355       {
356           std::cout
357               << "System topology:\n"
358               << "    Logical processor count: " << cds::OS::topology::processor_count() << "\n";
359           std::cout << std::endl;
360       }
361
362       {
363         CppUnitMini::TestCfg& cfg = CppUnitMini::TestCase::m_Cfg.get( "General" );
364         std::string strHZPScanStrategy = cfg.get( "HZP_scan_strategy", std::string("classic") );
365         if ( strHZPScanStrategy == "inplace" )
366             hzpGC.setScanType( cds::gc::hp::inplace );
367         else if ( strHZPScanStrategy == "classic" )
368             hzpGC.setScanType( cds::gc::hp::classic );
369         else {
370             std::cout << "Error value of HZP_scan_strategy in General section of test config\n";
371         }
372
373         switch (hzpGC.getScanType()) {
374         case cds::gc::hp::inplace:
375             std::cout << "Use in-place scan strategy for Hazard Pointer memory reclamation algorithm\n";
376             break;
377         case cds::gc::hp::classic:
378             std::cout << "Use classic scan strategy for Hazard Pointer memory reclamation algorithm\n";
379             break;
380         default:
381             std::cout << "ERROR: use unknown scan strategy for Hazard Pointer memory reclamation algorithm\n";
382             break;
383         }
384
385         std::cout << "     Hazard Pointer count: " << hzpGC.max_hazard_count() << "\n"
386                   << "  Max thread count for HP: " << hzpGC.max_thread_count() << "\n"
387                   << "Retired HP array capacity: " << hzpGC.retired_array_capacity() << "\n";
388       }
389
390       if ( CppUnitMini::TestCase::m_bPrintGCState ) {
391         cds::gc::hp::GarbageCollector::InternalState stat;
392         cds::gc::hp::GarbageCollector::instance().getInternalState( stat );
393
394         std::cout << "HP constants:"
395             << "\n\tHP count per thread=" << stat.nHPCount
396             << "\n\tMax thread count=" << stat.nMaxThreadCount
397             << "\n\tMax retired pointer count per thread=" << stat.nMaxRetiredPtrCount
398             << "\n\tHP record size in bytes=" << stat.nHPRecSize
399             << "\n" << std::endl;
400       }
401
402     // Attach main thread to CDS GC
403     cds::threading::Manager::attachThread();
404
405     if (xtestName[0] != 0)
406         num_errors = CppUnitMini::TestCase::run(reporter, xtestName, true);
407     else
408         num_errors = CppUnitMini::TestCase::run(reporter, testName);
409
410     // Detach main thread from CDS GC
411     cds::threading::Manager::detachThread();
412
413   }
414
415   // Finalize CDS runtime
416   cds::Terminate();
417
418   reporter->printSummary();
419   delete reporter;
420
421   return num_errors;
422 }
423
424 // See doc/README.intel for explanation about this code
425 #if defined (STLPORT) && defined (__ICL) && (__ICL >= 900) && \
426             (_STLP_MSVC_LIB < 1300) && defined (_STLP_USE_DYNAMIC_LIB)
427 #  include <exception>
428
429 #  undef std
430 namespace std
431 {
432   void _STLP_CALL unexpected() {
433     unexpected_handler hdl;
434     set_unexpected(hdl = set_unexpected((unexpected_handler)0));
435     hdl();
436   }
437 }
438 #endif