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