1 /*
2    Copyright (c) 2003, 2021, Oracle and/or its affiliates.
3     All rights reserved. Use is subject to license terms.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License, version 2.0,
7    as published by the Free Software Foundation.
8 
9    This program is also distributed with certain software (including
10    but not limited to OpenSSL) that is licensed under separate terms,
11    as designated in a particular file or component or in included license
12    documentation.  The authors of MySQL hereby grant you an additional
13    permission to link the program and your derivative works with the
14    separately licensed software that they have included with MySQL.
15 
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License, version 2.0, for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 */
25 
26 /**
27  *  NdbPortLibTest.cpp
28  *  Test the functionality of portlib
29  *  TODO - Add tests for NdbMem
30  */
31 
32 #include <ndb_global.h>
33 
34 #include "NdbOut.hpp"
35 #include "NdbThread.h"
36 #include "NdbMutex.h"
37 #include "NdbCondition.h"
38 #include "NdbSleep.h"
39 #include "NdbTick.h"
40 #include "NdbEnv.h"
41 #include "NdbHost.h"
42 #include "NdbMain.h"
43 
44 int TestHasFailed;
45 int verbose = 0;
46 
fail(const char * test,const char * cause)47 static void fail(const char* test, const char* cause)
48 {
49   TestHasFailed = 1;
50   ndbout << test << " failed, " << cause << endl;
51 }
52 
53 // test 1 variables and funcs
54 
thread1func(void * arg)55 extern "C"  void* thread1func(void* arg)
56 {
57   int arg1;
58   int returnvalue = 8;
59   arg1 = *(int*)arg;
60   ndbout << "thread1: thread1func called with arg = " << arg1 << endl;
61 
62   //  delay(1000);
63   if (arg1 != 7)
64     fail("TEST1", "Wrong arg");
65 
66   return (void*) returnvalue;
67 }
68 
69 // test 2 variables and funcs
70 
71 NdbMutex* test2mutex;
72 
test2func(void * arg)73 extern "C" void* test2func(void* arg)
74 {
75 
76   int arg1;
77   arg1 = *(int*)arg;
78   ndbout << "thread" << arg1 << " started in test2func" << endl;
79 
80   if (NdbMutex_Lock(test2mutex) != 0)
81     fail("TEST2", "Failed to lock mutex");
82 
83   ndbout << "thread" << arg1 << ", test2func " << endl;
84 
85   if (NdbMutex_Unlock(test2mutex) != 0)
86     fail("TEST2", "Failed to unlock mutex");
87 
88   int returnvalue = arg1;
89   return (void*) returnvalue;
90 }
91 
92 
93 // test 3 and 7 variables and funcs
94 
95 NdbMutex* testmutex;
96 NdbCondition* testcond;
97 int testthreadsdone;
98 
testfunc(void * arg)99 extern "C" void* testfunc(void* arg)
100 {
101   int tmpVar;
102   int threadno;
103 
104   threadno = *(int*)arg;
105 
106   ndbout << "Thread" << threadno << " started in testfunc" << endl;
107   do
108     {
109 
110       if ((threadno % 2) == 0)
111 	NdbSleep_SecSleep(1);
112       else
113 	NdbSleep_MilliSleep(100);
114 
115       if (NdbMutex_Lock(testmutex) != 0)
116 	fail("TEST3", "Wrong result from NdbMutex_Lock function");
117 
118       ndbout << "thread" << threadno << ", testfunc " << endl;
119       testthreadsdone++;
120       tmpVar = testthreadsdone;
121 
122       if (NdbCondition_Signal(testcond) != 0)
123 	fail("TEST3", "Wrong result from NdbCondition_Signal function");
124 
125       if (NdbMutex_Unlock(testmutex) != 0)
126 	fail("TEST3", "Wrong result from NdbMutex_Unlock function");
127 
128     }
129   while(tmpVar<100);
130 
131   return 0;
132 }
133 
testTryLockfunc(void * arg)134 extern "C" void* testTryLockfunc(void* arg)
135 {
136   int tmpVar = 0;
137   int threadno;
138 
139   threadno = *(int*)arg;
140 
141   ndbout << "Thread" << threadno << " started" << endl;
142   do
143     {
144 
145       if ((threadno % 2) == 0)
146 	NdbSleep_SecSleep(1);
147       else
148 	NdbSleep_MilliSleep(100);
149 
150       if (NdbMutex_Trylock(testmutex) == 0){
151 
152 	ndbout << "thread" << threadno << ", testTryLockfunc locked" << endl;
153   	testthreadsdone++;
154 	tmpVar = testthreadsdone;
155 
156 	if (NdbCondition_Signal(testcond) != 0)
157 	  fail("TEST3", "Wrong result from NdbCondition_Signal function");
158 
159 	if (NdbMutex_Unlock(testmutex) != 0)
160 	  fail("TEST3", "Wrong result from NdbMutex_Unlock function");
161       }
162 
163     }
164   while(tmpVar<100);
165 
166   return 0;
167 }
168 
169 
170 
171 void testMicros(int count);
172 Uint64 time_diff(Uint64 s1, Uint64 s2, Uint32 m1, Uint32 m2);
173 
174 NDB_COMMAND(PortLibTest, "portlibtest", "portlibtest", "Test the portable function layer", 4096){
175 
176   ndbout << "= TESTING ARGUMENT PASSING ============" << endl;
177   ndbout << "ARGC: " << argc << endl;
178   for(int i = 1; i < argc; i++){
179     ndbout << " ARGV"<<i<<": " << (char*)argv[i] << endl;
180   }
181   ndbout << endl << endl;
182 
183 
184   struct NdbThread* thread1var;
185   void *status = 0;
186   int arg = 7;
187 
188   TestHasFailed = 0;
189   // create one thread and wait for it to return
190   ndbout << "= TEST1 ===============================" << endl;
191 
192   thread1var = NdbThread_Create(thread1func, // Function
193 				(void**)&arg,// Arg
194 				2048,        // Stacksize
195 				(char*)"thread1",  // Thread name
196 				NDB_THREAD_PRIO_MEAN); // Thread priority
197 
198 
199   if(NdbThread_WaitFor(thread1var, &status) != 0)
200     fail("TEST1", "NdbThread_WaitFor failed");
201   // NOTE! thread return value is not yet used in Ndb and thus not tested(does not work)
202   //ndbout << "thread1 returned, status = " << status << endl;
203   //if (status != 8)
204   // fail("TEST1", "Wrong status");
205   ndbout << "TEST1 completed" << endl;
206 
207 
208   NdbThread_Destroy(&thread1var);
209 
210   // Create 10 threads that will wait for a mutex before printing it's message to screen
211   ndbout << "= TEST2 ===============================" << endl;
212 #define T2_THREADS 10
213   NdbThread* threads[T2_THREADS];
214   int   args[T2_THREADS];
215   void *status2 = 0;
216   test2mutex = NdbMutex_Create();
217   NdbMutex_Lock(test2mutex);
218 
219   for (int i = 0; i < T2_THREADS; i++)
220     {
221       args[i] = i;
222     threads[i] = NdbThread_Create(test2func, // Function
223 				  (void**)&args[i],// Arg
224 				  2048,        // Stacksize
225 				  (char*)"test2thread",  // Thread name
226 				  NDB_THREAD_PRIO_MEAN); // Thread priority
227     if (threads[i] == NULL)
228       fail("TEST2", "NdbThread_Create failed");
229     }
230 
231   ndbout << "All threads created" << endl;
232 
233   NdbMutex_Unlock(test2mutex);
234 
235   for (int i = 0; i < T2_THREADS; i++)
236   {
237     if (NdbThread_WaitFor(threads[i], &status2))
238       fail("TEST2", "NdbThread_WaitFor failed");
239 
240     NdbThread_Destroy(&threads[i]);
241     // Don't test return values
242     //    ndbout << "thread" << i << " returned, status = " << status2 << endl;
243     //    if (status2 != i)
244     //      fail("TEST2", "Wrong status");
245   }
246 
247   if (NdbMutex_Lock(test2mutex) != 0)
248     fail("TEST2", "NdbMutex_Lock failed");
249   if (NdbMutex_Unlock(test2mutex) != 0)
250     fail("TEST2", "NdbMutex_Unlock failed");
251   if (NdbMutex_Destroy(test2mutex) != 0)
252     fail("TEST2", "NdbMutex_Destroy failed");
253   ndbout << "TEST2 completed" << endl;
254 
255   ndbout << "= TEST3 ===============================" << endl;
256   // Create 10 threads that will by synchronised by a condition
257   // When they are awakened and have the mutex they will increment a global variable
258 #define T3_THREADS 10
259   NdbThread* t3threads[T3_THREADS];
260   int   t3args[T3_THREADS];
261   void *status3 = 0;
262 
263   testmutex = NdbMutex_Create();
264   testcond = NdbCondition_Create();
265   testthreadsdone = 0;
266 
267   for (int i = 0; i < T3_THREADS; i++)
268     {
269       t3args[i] = i;
270       t3threads[i] = NdbThread_Create(testfunc, // Function
271 				      (void**)&t3args[i],// Arg
272 				      2048,        // Stacksize
273 				      (char*)"test3thread",  // Thread name
274 				      NDB_THREAD_PRIO_MEAN); // Thread priority
275     }
276 
277   ndbout << "All threads created" << endl;
278 
279   if (NdbMutex_Lock(testmutex) != 0)
280     fail("TEST3", "NdbMutex_Lock failed");
281 
282   while (testthreadsdone < T3_THREADS*10)
283     {
284       if(NdbCondition_Wait(testcond, testmutex) != 0)
285 	fail("TEST3", "NdbCondition_Wait failed");
286       ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
287     }
288   if (NdbMutex_Unlock(testmutex) != 0)
289     fail("TEST3", "NdbMutex_Unlock failed");
290 
291   for (int i = 0; i < T3_THREADS; i++)
292   {
293     if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
294       fail("TEST3", "NdbThread_WaitFor failed");
295 
296     NdbThread_Destroy(&t3threads[i]);
297     //ndbout << "thread" << i << " returned, status = " << status3 << endl;
298     //if (status3 != i)
299     //  fail("TEST3", "Wrong status");
300   }
301 
302   NdbMutex_Destroy(testmutex);
303   NdbCondition_Destroy(testcond);
304   ndbout << "TEST3 completed" << endl;
305 
306   ndbout << "= TEST4 ===============================" << endl;
307   // Check tick functions
308 
309   //#if 0
310 
311   int sleeptimes[] = {78, 12, 199, 567, 899};
312 
313 
314   for (int i = 0; i < 5; i++)
315   {
316   ndbout << "*------------------------------- Measure" << i << endl;
317 
318   Uint64 millisec_now;
319   Uint64 millisec_now2;
320 
321   millisec_now = NdbTick_CurrentMillisecond();
322   NdbSleep_MilliSleep(sleeptimes[i]);
323   millisec_now2 = NdbTick_CurrentMillisecond();
324 
325   ndbout << "  Time before sleep = " << millisec_now << endl;
326   ndbout << "  Time after sleep =  " << millisec_now2 << endl;
327   ndbout << "  Tried to sleep "<<sleeptimes[i]<<" milliseconds." << endl;
328   ndbout << "  Sleep time was " << millisec_now2 -millisec_now <<" milliseconds." << endl;
329 
330   }
331 
332   ndbout << "TEST4 completed" << endl;
333 
334   ndbout << "= TEST5 ===============================" << endl;
335   // Check NdbOut
336 
337   ndbout << "Testing hex and dec functions of NdbOut" << endl;
338 
339   for (int i = 0; i<= 0xFF; i++)
340     {
341       ndbout << i << "=" <<hex << i << "="<<dec << i << ", ";
342     }
343 
344   ndbout << endl<< "Testing that hex is reset to dec by endl" << endl;
345   ndbout << hex << 67 << endl;
346   ndbout << 67 << endl;
347 
348   ndbout << "TEST5 completed" << endl;
349 
350 
351   ndbout << "= TEST6 ===============================" << endl;
352   const char* theEnvHostNamePtr;
353   char buf[255];
354   char theHostHostName[256];
355   theEnvHostNamePtr = NdbEnv_GetEnv("HOSTNAME", buf, 255);
356   if(theEnvHostNamePtr == NULL)
357     fail("TEST6", "Could not get HOSTNAME from env");
358   else{
359     ndbout << "HOSTNAME from GetEnv" <<  theEnvHostNamePtr << endl;
360 
361     NdbHost_GetHostName(theHostHostName);
362 
363     ndbout << "HOSTNAME from GetHostName" <<theHostHostName << endl;
364 
365     if (strcmp(theEnvHostNamePtr, theHostHostName) != 0)
366       fail("TEST6", "NdbHost_GetHostName or NdbEnv_GetEnv failed");
367   }
368 
369   ndbout << "= TEST7 ===============================" << endl;
370 
371   testmutex = NdbMutex_Create();
372   testcond = NdbCondition_Create();
373   testthreadsdone = 0;
374 
375   for (int i = 0; i < T3_THREADS; i++)
376     {
377       t3args[i] = i;
378       t3threads[i] = NdbThread_Create(testfunc, // Function
379 				      (void**)&t3args[i],// Arg
380 				      2048,        // Stacksize
381 				      (char*)"test7thread",  // Thread name
382 				      NDB_THREAD_PRIO_MEAN); // Thread priority
383     }
384 
385   ndbout << "All threads created" << endl;
386 
387   if (NdbMutex_Lock(testmutex) != 0)
388     fail("TEST7", "NdbMutex_Lock failed");
389 
390   while (testthreadsdone < T3_THREADS*10)
391     {
392       // just testing the functionality without timing out, therefor 20 sec.
393       if(NdbCondition_WaitTimeout(testcond, testmutex, 20000) != 0)
394 	fail("TEST7", "NdbCondition_WaitTimeout failed");
395       ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
396     }
397   if (NdbMutex_Unlock(testmutex) != 0)
398     fail("TEST7", "NdbMutex_Unlock failed");
399 
400   for (int i = 0; i < T3_THREADS; i++)
401   {
402     if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
403       fail("TEST7", "NdbThread_WaitFor failed");
404 
405     NdbThread_Destroy(&t3threads[i]);
406   }
407 
408   NdbMutex_Destroy(testmutex);
409   NdbCondition_Destroy(testcond);
410 
411   ndbout << "TEST7 completed" << endl;
412 
413 
414   ndbout << "= TEST8 ===============================" << endl;
415   ndbout << "         NdbCondition_WaitTimeout" << endl;
416   testmutex = NdbMutex_Create();
417   testcond = NdbCondition_Create();
418 
419   for (int i = 0; i < 5; i++)
420   {
421     ndbout << "*------------------------------- Measure" << i << endl;
422 
423   Uint64 millisec_now;
424   Uint64 millisec_now2;
425 
426   millisec_now = NdbTick_CurrentMillisecond();
427   if (NdbCondition_WaitTimeout(testcond, testmutex, sleeptimes[i]) != 0)
428     fail("TEST8", "NdbCondition_WaitTimeout failed");
429   millisec_now2 = NdbTick_CurrentMillisecond();
430 
431   ndbout << "  Time before WaitTimeout = " << millisec_now << endl;
432   ndbout << "  Time after WaitTimeout =  " << millisec_now2 << endl;
433   ndbout << "  Tried to wait "<<sleeptimes[i]<<" milliseconds." << endl;
434   ndbout << "  Wait time was " << millisec_now2 -millisec_now <<" milliseconds." << endl;
435 
436   }
437 
438   ndbout << "TEST8 completed" << endl;
439 
440 
441   ndbout << "= TEST9 ===============================" << endl;
442   ndbout << "         NdbTick_CurrentXXXXXsecond compare" << endl;
443 
444   for (int i = 0; i < 5; i++)
445   {
446     ndbout << "*------------------------------- Measure" << i << endl;
447 
448   const NDB_TICKS t1 = NdbTick_getCurrentTicks();
449   NdbSleep_MilliSleep(sleeptimes[i]);
450   const NDB_TICKS t2 = NdbTick_getCurrentTicks();
451 
452   Uint64 usecdiff = NdbTick_Elapsed(t1,t2).microSec();
453   Uint64 msecdiff = NdbTick_Elapsed(t1,t2).milliSec();
454 
455   ndbout << "     Slept "<<sleeptimes[i]<<" milliseconds." << endl;
456   ndbout << "  Measured " << msecdiff <<" milliseconds with milli function ." << endl;
457   ndbout << "  Measured " << usecdiff/1000 << "," << usecdiff%1000<<" milliseconds with micro function ." << endl;
458   }
459 
460   ndbout << "TEST9 completed" << endl;
461 
462 
463   const int iter = 20;
464   ndbout << "Testing microsecond timer - " << iter << " iterations" << endl;
465   testMicros(iter);
466   ndbout << "Testing microsecond timer - COMPLETED" << endl;
467 
468   ndbout << "= TEST10 ===============================" << endl;
469 
470   testmutex = NdbMutex_Create();
471   testcond = NdbCondition_Create();
472   testthreadsdone = 0;
473 
474   for (int i = 0; i < T3_THREADS; i++)
475     {
476       t3args[i] = i;
477       t3threads[i] = NdbThread_Create(testTryLockfunc, // Function
478 				      (void**)&t3args[i],// Arg
479 				      2048,        // Stacksize
480 				      (char*)"test10thread",  // Thread name
481 				      NDB_THREAD_PRIO_MEAN); // Thread priority
482     }
483 
484   ndbout << "All threads created" << endl;
485 
486   if (NdbMutex_Lock(testmutex) != 0)
487     fail("TEST10", "NdbMutex_Lock failed");
488 
489   while (testthreadsdone < T3_THREADS*10)
490     {
491       if(NdbCondition_Wait(testcond, testmutex) != 0)
492 	fail("TEST10", "NdbCondition_WaitTimeout failed");
493       ndbout << "Condition signaled, there are " << testthreadsdone << " completed threads" << endl;
494     }
495   if (NdbMutex_Unlock(testmutex) != 0)
496     fail("TEST10", "NdbMutex_Unlock failed");
497 
498   for (int i = 0; i < T3_THREADS; i++)
499   {
500     if (NdbThread_WaitFor(t3threads[i], &status3) != 0)
501       fail("TEST10", "NdbThread_WaitFor failed");
502 
503     NdbThread_Destroy(&t3threads[i]);
504   }
505 
506   NdbMutex_Destroy(testmutex);
507   NdbCondition_Destroy(testcond);
508 
509   ndbout << "TEST10 completed" << endl;
510 
511 
512   // Check total status of test
513 
514   if (TestHasFailed == 1)
515     ndbout << endl << "TEST FAILED!" << endl;
516   else
517     ndbout << endl << "TEST PASSED!" << endl;
518 
519   return TestHasFailed;
520 
521 };
522 
time_diff(Uint64 s1,Uint64 s2,Uint32 m1,Uint32 m2)523 Uint64 time_diff(Uint64 s1, Uint64 s2, Uint32 m1, Uint32 m2){
524 
525   Uint64 diff = 0;
526   diff += (s2 - s1) * 1000000;
527   if(m2 >= m1)
528     diff += (m2 - m1);
529   else {
530     diff += m2;
531     diff -= m1;
532   }
533 
534   //  if(0)
535   // ndbout("(s1,m1) = (%d, %d) (s2,m2) = (%d, %d) -> diff = %d\n",
536   //   (Uint32)s1,m1,(Uint32)s2,m2, (Uint32)diff);
537 
538   return diff;
539 };
540 
541 void
testMicros(int count)542 testMicros(int count){
543   Uint32 avg = 0;
544   Uint32 sum2 = 0;
545 
546   for(int i = 0; i<count; i++){
547 
548     const NDB_TICKS t1 = NdbTick_getCurrentTicks();
549 
550     Uint32 r = (rand() % 1000) + 1;
551     NdbSleep_MilliSleep(r);
552 
553     const NDB_TICKS t2 = NdbTick_getCurrentTicks();
554     const Uint64 m = NdbTick_Elapsed(t1,t2).microSec();
555     if(verbose)
556       ndbout << "Slept for " << r << " ms"
557 	     << " - Measured  " << m << " us" << endl;
558 
559     if(m > (r*1000)){
560       avg += (m - (r*1000));
561       sum2 += (m - (r*1000)) * (m - (r*1000));
562     } else {
563       avg += ((r*1000) - m);
564       sum2 += ((r*1000) - m) * ((r*1000) - m);
565     }
566 #if 0
567     m /= 1000;
568     if(m > r && ((m - r) > 10)){
569       ndbout << "Difference to big: " << (m - r) << " - Test failed" << endl;
570       TestHasFailed = 1;
571     }
572     if(m < r && ((r - m) > 10)){
573       ndbout << "Difference to big: " << (r - m) << " - Test failed" << endl;
574       TestHasFailed = 1;
575     }
576 #endif
577   }
578 
579   Uint32 dev = (avg * avg - sum2) / count; dev /= count;
580   avg /= count;
581 
582   Uint32 t = 0;
583   while((t*t)<dev) t++;
584   ndbout << "NOTE - measure are compared to NdbSleep_MilliSleep(...)" << endl;
585   ndbout << "Average error = " << avg << " us" << endl;
586   ndbout << "Stddev  error = " << t << " us" << endl;
587 }
588