1 /*
2     Copyright (c) 2005-2020 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 //! \file test_malloc_compliance.cpp
18 //! \brief Test for [memory_allocation.scalable_alloc_c_interface] functionality
19 
20 #define __TBB_NO_IMPLICIT_LINKAGE 1
21 
22 #define __STDC_LIMIT_MACROS 1 // to get SIZE_MAX from stdint.h
23 
24 #include "common/test.h"
25 
26 #include "common/utils.h"
27 #include "common/utils_report.h"
28 #include "common/spin_barrier.h"
29 #include "common/memory_usage.h"
30 
31 #include "oneapi/tbb/detail/_config.h"
32 
33 #define __TBB_NO_IMPLICIT_LINKAGE 1
34 #include "tbb/scalable_allocator.h"
35 
36 #include <vector>
37 
38 #if _WIN32 || _WIN64
39 /**
40  *  _WIN32_WINNT should be defined at the very beginning,
41  *  because other headers might include <windows.h>
42  **/
43 #undef _WIN32_WINNT
44 #define _WIN32_WINNT 0x0501
45 #include <windows.h>
46 #include <stdio.h>
47 
48 #if _MSC_VER && defined(_MT) && defined(_DLL)
49     #pragma comment(lib, "version.lib")  // to use GetFileVersionInfo*
50 #endif
51 
52 void limitMem( size_t limit )
53 {
54     static HANDLE hJob = NULL;
55     JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo;
56 
57     jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
58     jobInfo.ProcessMemoryLimit = limit? limit*MByte : 2*MByte*1024;
59     if (NULL == hJob) {
60         if (NULL == (hJob = CreateJobObject(NULL, NULL))) {
61             REPORT("Can't assign create job object: %ld\n", GetLastError());
62             exit(1);
63         }
64         if (0 == AssignProcessToJobObject(hJob, GetCurrentProcess())) {
65             REPORT("Can't assign process to job object: %ld\n", GetLastError());
66             exit(1);
67         }
68     }
69     if (0 == SetInformationJobObject(hJob, JobObjectExtendedLimitInformation,
70                                      &jobInfo, sizeof(jobInfo))) {
71         REPORT("Can't set limits: %ld\n", GetLastError());
72         exit(1);
73     }
74 }
75 // Do not test errno with static VC runtime
76 #else // _WIN32 || _WIN64
77 #include <sys/resource.h>
78 #include <stdlib.h>
79 #include <stdio.h>
80 #include <errno.h>
81 #include <sys/types.h>  // uint64_t on FreeBSD, needed for rlim_t
82 #include <stdint.h>     // SIZE_MAX
83 
84 void limitMem( size_t limit )
85 {
86     rlimit rlim;
87     int ret = getrlimit(RLIMIT_AS,&rlim);
88     if (0 != ret) {
89         REPORT("getrlimit() returned an error: errno %d\n", errno);
90         exit(1);
91     }
92     if (rlim.rlim_max==(rlim_t)RLIM_INFINITY)
93         rlim.rlim_cur = (limit > 0) ? limit*MByte : rlim.rlim_max;
94     else rlim.rlim_cur = (limit > 0 && limit<rlim.rlim_max) ? limit*MByte : rlim.rlim_max;
95     ret = setrlimit(RLIMIT_AS,&rlim);
96     if (0 != ret) {
97         REPORT("Can't set limits: errno %d\n", errno);
98         exit(1);
99     }
100 }
101 #endif  // _WIN32 || _WIN64
102 
103 bool __tbb_test_errno = false;
104 
105 #define ASSERT_ERRNO(cond, msg)  REQUIRE_MESSAGE( (!__tbb_test_errno || (cond)), msg )
106 #define CHECK_ERRNO(cond) (__tbb_test_errno && (cond))
107 
108 static const int MinThread = 1;
109 static const int MaxThread = 4;
110 static bool Verbose = false;
111 
112 #include <time.h>
113 #include <errno.h>
114 #include <limits.h> // for CHAR_BIT
115 
116 #if __linux__
117 #include <stdint.h> // uintptr_t
118 #endif
119 #if _WIN32 || _WIN64
120 #include <malloc.h> // _aligned_(malloc|free|realloc)
121 #if __MINGW64__
122 // Workaround a bug in MinGW64 headers with _aligned_(malloc|free) not declared by default
123 extern "C" void __cdecl _aligned_free(void *);
124 extern "C" void *__cdecl _aligned_malloc(size_t,size_t);
125 #endif
126 #endif
127 
128 #include <vector>
129 
130 const int COUNT_ELEM = 25000;
131 const size_t MAX_SIZE = 1000;
132 const int COUNTEXPERIMENT = 10000;
133 
134 const char strError[]="failed";
135 const char strOk[]="done";
136 
137 typedef unsigned int UINT;
138 typedef unsigned char UCHAR;
139 typedef unsigned long DWORD;
140 typedef unsigned char BYTE;
141 
142 typedef void* TestMalloc(size_t size);
143 typedef void* TestCalloc(size_t num, size_t size);
144 typedef void* TestRealloc(void* memblock, size_t size);
145 typedef void  TestFree(void* memblock);
146 typedef int   TestPosixMemalign(void **memptr, size_t alignment, size_t size);
147 typedef void* TestAlignedMalloc(size_t size, size_t alignment);
148 typedef void* TestAlignedRealloc(void* memblock, size_t size, size_t alignment);
149 typedef void  TestAlignedFree(void* memblock);
150 
151 // pointers to tested functions
152 TestMalloc*  Rmalloc;
153 TestCalloc*  Rcalloc;
154 TestRealloc* Rrealloc;
155 TestFree*    Tfree;
156 TestPosixMemalign*  Rposix_memalign;
157 TestAlignedMalloc*  Raligned_malloc;
158 TestAlignedRealloc* Raligned_realloc;
159 TestAlignedFree* Taligned_free;
160 
161 // call functions via pointer and check result's alignment
162 void* Tmalloc(size_t size);
163 void* Tcalloc(size_t num, size_t size);
164 void* Trealloc(void* memblock, size_t size);
165 int   Tposix_memalign(void **memptr, size_t alignment, size_t size);
166 void* Taligned_malloc(size_t size, size_t alignment);
167 void* Taligned_realloc(void* memblock, size_t size, size_t alignment);
168 
169 bool error_occurred = false;
170 
171 #if __APPLE__
172 // Tests that use the variables are skipped on macOS*
173 #else
174 const size_t COUNT_ELEM_CALLOC = 2;
175 const int COUNT_TESTS = 1000;
176 static bool perProcessLimits = true;
177 #endif
178 
179 const size_t POWERS_OF_2 = 20;
180 
181 struct MemStruct
182 {
183     void* Pointer;
184     UINT Size;
185 
186     MemStruct() : Pointer(NULL), Size(0) {}
187     MemStruct(void* ptr, UINT sz) : Pointer(ptr), Size(sz) {}
188 };
189 
190 class CMemTest: utils::NoAssign
191 {
192     UINT CountErrors;
193     bool FullLog;
194     utils::SpinBarrier *limitBarrier;
195     static bool firstTime;
196 
197 public:
198     CMemTest(utils::SpinBarrier *barrier, bool isVerbose=false) :
199         CountErrors(0), limitBarrier(barrier)
200         {
201             srand((UINT)time(NULL));
202             FullLog=isVerbose;
203         }
204     void NULLReturn(UINT MinSize, UINT MaxSize, int total_threads); // NULL pointer + check errno
205     void UniquePointer(); // unique pointer - check with padding
206     void AddrArifm(); // unique pointer - check with pointer arithmetic
207     bool ShouldReportError();
208     void Free_NULL(); //
209     void Zerofilling(); // check if arrays are zero-filled
210     void TestAlignedParameters();
211     void RunAllTests(int total_threads);
212     ~CMemTest() {}
213 };
214 
215 class Limit {
216     size_t limit;
217 public:
218     Limit(size_t a_limit) : limit(a_limit) {}
219     void operator() () const {
220         limitMem(limit);
221     }
222 };
223 
224 int argC;
225 char** argV;
226 
227 struct RoundRobin: utils::NoAssign {
228     const long number_of_threads;
229     mutable CMemTest test;
230 
231     RoundRobin( long p, utils::SpinBarrier *limitBarrier, bool verbose ) :
232         number_of_threads(p), test(limitBarrier, verbose) {}
233     void operator()( int /*id*/ ) const
234         {
235             test.RunAllTests(number_of_threads);
236         }
237 };
238 
239 bool CMemTest::firstTime = true;
240 
241 inline size_t choose_random_alignment() {
242     return sizeof(void*)<<(rand() % POWERS_OF_2);
243 }
244 
245 #if TBB_REVAMP_TODO
246 // TODO: enable this test mode
247 static void setSystemAllocs()
248 {
249     Rmalloc=malloc;
250     Rrealloc=realloc;
251     Rcalloc=calloc;
252     Tfree=free;
253 #if _WIN32 || _WIN64
254     Raligned_malloc=_aligned_malloc;
255     Raligned_realloc=_aligned_realloc;
256     Taligned_free=_aligned_free;
257     Rposix_memalign=0;
258 #elif  __APPLE__ || __sun || __ANDROID__
259 // macOS, Solaris*, and Android* don't have posix_memalign
260     Raligned_malloc=0;
261     Raligned_realloc=0;
262     Taligned_free=0;
263     Rposix_memalign=0;
264 #else
265     Raligned_malloc=0;
266     Raligned_realloc=0;
267     Taligned_free=0;
268     Rposix_memalign=posix_memalign;
269 #endif
270 }
271 #endif
272 
273 // check that realloc works as free and as malloc
274 void ReallocParam()
275 {
276     const int ITERS = 1000;
277     int i;
278     void *bufs[ITERS];
279 
280     bufs[0] = Trealloc(NULL, 30*MByte);
281     REQUIRE_MESSAGE(bufs[0], "Can't get memory to start the test.");
282 
283     for (i=1; i<ITERS; i++)
284     {
285         bufs[i] = Trealloc(NULL, 30*MByte);
286         if (NULL == bufs[i])
287             break;
288     }
289     REQUIRE_MESSAGE(i<ITERS, "Limits should be decreased for the test to work.");
290 
291     Trealloc(bufs[0], 0);
292     /* There is a race for the free space between different threads at
293        this point. So, have to run the test sequentially.
294     */
295     bufs[0] = Trealloc(NULL, 30*MByte);
296     REQUIRE(bufs[0]);
297 
298     for (int j=0; j<i; j++)
299         Trealloc(bufs[j], 0);
300 }
301 
302 void CheckArgumentsOverflow()
303 {
304     void *p;
305     const size_t params[] = {SIZE_MAX, SIZE_MAX-16};
306 
307     for (unsigned i=0; i<utils::array_length(params); i++) {
308         p = Tmalloc(params[i]);
309         REQUIRE(!p);
310         ASSERT_ERRNO(errno==ENOMEM, NULL);
311         p = Trealloc(NULL, params[i]);
312         REQUIRE(!p);
313         ASSERT_ERRNO(errno==ENOMEM, NULL);
314         p = Tcalloc(1, params[i]);
315         REQUIRE(!p);
316         ASSERT_ERRNO(errno==ENOMEM, NULL);
317         p = Tcalloc(params[i], 1);
318         REQUIRE(!p);
319         ASSERT_ERRNO(errno==ENOMEM, NULL);
320     }
321     const size_t max_alignment = size_t(1) << (sizeof(size_t)*CHAR_BIT - 1);
322     if (Rposix_memalign) {
323         int ret = Rposix_memalign(&p, max_alignment, ~max_alignment);
324         REQUIRE(ret == ENOMEM);
325         for (unsigned i=0; i<utils::array_length(params); i++) {
326             ret = Rposix_memalign(&p, max_alignment, params[i]);
327             REQUIRE(ret == ENOMEM);
328             ret = Rposix_memalign(&p, sizeof(void*), params[i]);
329             REQUIRE(ret == ENOMEM);
330         }
331     }
332     if (Raligned_malloc) {
333         p = Raligned_malloc(~max_alignment, max_alignment);
334         REQUIRE(!p);
335         for (unsigned i=0; i<utils::array_length(params); i++) {
336             p = Raligned_malloc(params[i], max_alignment);
337             REQUIRE(!p);
338             ASSERT_ERRNO(errno==ENOMEM, NULL);
339             p = Raligned_malloc(params[i], sizeof(void*));
340             REQUIRE(!p);
341             ASSERT_ERRNO(errno==ENOMEM, NULL);
342         }
343     }
344 
345     p = Tcalloc(SIZE_MAX/2-16, SIZE_MAX/2-16);
346     REQUIRE(!p);
347     ASSERT_ERRNO(errno==ENOMEM, NULL);
348     p = Tcalloc(SIZE_MAX/2, SIZE_MAX/2);
349     REQUIRE(!p);
350     ASSERT_ERRNO(errno==ENOMEM, NULL);
351 }
352 
353 void InvariantDataRealloc(bool aligned, size_t maxAllocSize, bool checkData)
354 {
355     utils::FastRandom<> fastRandom(1);
356     size_t size = 0, start = 0;
357     char *ptr = NULL,
358         // master to create copies and compare ralloc result against it
359         *master = (char*)Tmalloc(2*maxAllocSize);
360 
361     REQUIRE(master);
362     REQUIRE_MESSAGE(!(2*maxAllocSize%sizeof(unsigned short)),
363            "The loop below expects that 2*maxAllocSize contains sizeof(unsigned short)");
364     for (size_t k = 0; k<2*maxAllocSize; k+=sizeof(unsigned short))
365         *(unsigned short*)(master+k) = fastRandom.get();
366 
367     for (int i=0; i<100; i++) {
368         // don't want sizeNew==0 here
369         const size_t sizeNew = fastRandom.get() % (maxAllocSize-1) + 1;
370         char *ptrNew = aligned?
371             (char*)Taligned_realloc(ptr, sizeNew, choose_random_alignment())
372             : (char*)Trealloc(ptr, sizeNew);
373         REQUIRE(ptrNew);
374         // check that old data not changed
375         if (checkData)
376             REQUIRE_MESSAGE(!memcmp(ptrNew, master+start, utils::min(size, sizeNew)), "broken data");
377 
378         // prepare fresh data, copying them from random position in master
379         size = sizeNew;
380         ptr = ptrNew;
381         if (checkData) {
382             start = fastRandom.get() % maxAllocSize;
383             memcpy(ptr, master+start, size);
384         }
385     }
386     if (aligned)
387         Taligned_realloc(ptr, 0, choose_random_alignment());
388     else
389         Trealloc(ptr, 0);
390     Tfree(master);
391 }
392 
393 void CheckReallocLeak()
394 {
395     int i;
396     const int ITER_TO_STABILITY = 10;
397     // do bootstrap
398     for (int k=0; k<3; k++)
399         InvariantDataRealloc(/*aligned=*/false, 128*MByte, /*checkData=*/false);
400     size_t prev = utils::GetMemoryUsage(utils::peakUsage);
401     // expect realloc to not increase peak memory consumption after ITER_TO_STABILITY-1 iterations
402     for (i=0; i<ITER_TO_STABILITY; i++) {
403         for (int k=0; k<3; k++)
404             InvariantDataRealloc(/*aligned=*/false, 128*MByte, /*checkData=*/false);
405         size_t curr = utils::GetMemoryUsage(utils::peakUsage);
406         if (prev == curr)
407             break;
408         prev = curr;
409     }
410     REQUIRE_MESSAGE(i < ITER_TO_STABILITY, "Can't stabilize memory consumption.");
411 }
412 
413 // if non-zero byte found, returns bad value address plus 1
414 size_t NonZero(void *ptr, size_t size)
415 {
416     size_t words = size / sizeof(intptr_t);
417     size_t tailSz = size % sizeof(intptr_t);
418     intptr_t *buf =(intptr_t*)ptr;
419     char *bufTail =(char*)(buf+words);
420 
421     for (size_t i=0; i<words; i++)
422         if (buf[i]) {
423             for (unsigned b=0; b<sizeof(intptr_t); b++)
424                 if (((char*)(buf+i))[b])
425                     return sizeof(intptr_t)*i + b + 1;
426         }
427     for (size_t i=0; i<tailSz; i++)
428         if (bufTail[i]) {
429             return words*sizeof(intptr_t)+i+1;
430         }
431     return 0;
432 }
433 
434 struct TestStruct
435 {
436     DWORD field1:2;
437     DWORD field2:6;
438     double field3;
439     UCHAR field4[100];
440     TestStruct* field5;
441     std::vector<int> field7;
442     double field8;
443 };
444 
445 void* Tmalloc(size_t size)
446 {
447     // For compatibility, on 64-bit systems malloc should align to 16 bytes
448     size_t alignment = (sizeof(intptr_t)>4 && size>8) ? 16 : 8;
449     void *ret = Rmalloc(size);
450     if (0 != ret)
451         REQUIRE_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
452                "allocation result should be properly aligned");
453     return ret;
454 }
455 void* Tcalloc(size_t num, size_t size)
456 {
457     // For compatibility, on 64-bit systems calloc should align to 16 bytes
458     size_t alignment = (sizeof(intptr_t)>4 && num && size>8) ? 16 : 8;
459     void *ret = Rcalloc(num, size);
460     if (0 != ret)
461         REQUIRE_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
462                "allocation result should be properly aligned");
463     return ret;
464 }
465 void* Trealloc(void* memblock, size_t size)
466 {
467     // For compatibility, on 64-bit systems realloc should align to 16 bytes
468     size_t alignment = (sizeof(intptr_t)>4 && size>8) ? 16 : 8;
469     void *ret = Rrealloc(memblock, size);
470     if (0 != ret)
471         REQUIRE_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
472                "allocation result should be properly aligned");
473     return ret;
474 }
475 int Tposix_memalign(void **memptr, size_t alignment, size_t size)
476 {
477     int ret = Rposix_memalign(memptr, alignment, size);
478     if (0 == ret)
479         REQUIRE_MESSAGE(0==((uintptr_t)*memptr & (alignment-1)),
480                "allocation result should be aligned");
481     return ret;
482 }
483 void* Taligned_malloc(size_t size, size_t alignment)
484 {
485     void *ret = Raligned_malloc(size, alignment);
486     if (0 != ret)
487         REQUIRE_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
488                "allocation result should be aligned");
489     return ret;
490 }
491 void* Taligned_realloc(void* memblock, size_t size, size_t alignment)
492 {
493     void *ret = Raligned_realloc(memblock, size, alignment);
494     if (0 != ret)
495         REQUIRE_MESSAGE(0==((uintptr_t)ret & (alignment-1)),
496                "allocation result should be aligned");
497     return ret;
498 }
499 
500 struct PtrSize {
501     void  *ptr;
502     size_t size;
503 };
504 
505 static int cmpAddrs(const void *p1, const void *p2)
506 {
507     const PtrSize *a = (const PtrSize *)p1;
508     const PtrSize *b = (const PtrSize *)p2;
509 
510     return a->ptr < b->ptr ? -1 : ( a->ptr == b->ptr ? 0 : 1);
511 }
512 
513 void CMemTest::AddrArifm()
514 {
515     PtrSize *arr = (PtrSize*)Tmalloc(COUNT_ELEM*sizeof(PtrSize));
516 
517     if (FullLog) REPORT("\nUnique pointer using Address arithmetic\n");
518     if (FullLog) REPORT("malloc....");
519     REQUIRE(arr);
520     for (int i=0; i<COUNT_ELEM; i++)
521     {
522         arr[i].size=rand()%MAX_SIZE;
523         arr[i].ptr=Tmalloc(arr[i].size);
524     }
525     qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
526 
527     for (int i=0; i<COUNT_ELEM-1; i++)
528     {
529         if (NULL!=arr[i].ptr && NULL!=arr[i+1].ptr)
530             REQUIRE_MESSAGE((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
531                    "intersection detected");
532     }
533     //----------------------------------------------------------------
534     if (FullLog) REPORT("realloc....");
535     for (int i=0; i<COUNT_ELEM; i++)
536     {
537         size_t count=arr[i].size*2;
538         void *tmpAddr=Trealloc(arr[i].ptr,count);
539         if (NULL!=tmpAddr) {
540             arr[i].ptr = tmpAddr;
541             arr[i].size = count;
542         } else if (count==0) { // because realloc(..., 0) works as free
543             arr[i].ptr = NULL;
544             arr[i].size = 0;
545         }
546     }
547     qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
548 
549     for (int i=0; i<COUNT_ELEM-1; i++)
550     {
551         if (NULL!=arr[i].ptr && NULL!=arr[i+1].ptr)
552             REQUIRE_MESSAGE((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
553                    "intersection detected");
554     }
555     for (int i=0; i<COUNT_ELEM; i++)
556     {
557         Tfree(arr[i].ptr);
558     }
559     //-------------------------------------------
560     if (FullLog) REPORT("calloc....");
561     for (int i=0; i<COUNT_ELEM; i++)
562     {
563         arr[i].size=rand()%MAX_SIZE;
564         arr[i].ptr=Tcalloc(arr[i].size,1);
565     }
566     qsort(arr, COUNT_ELEM, sizeof(PtrSize), cmpAddrs);
567 
568     for (int i=0; i<COUNT_ELEM-1; i++)
569     {
570         if (NULL!=arr[i].ptr && NULL!=arr[i+1].ptr)
571             REQUIRE_MESSAGE((uintptr_t)arr[i].ptr+arr[i].size <= (uintptr_t)arr[i+1].ptr,
572                    "intersection detected");
573     }
574     for (int i=0; i<COUNT_ELEM; i++)
575     {
576         Tfree(arr[i].ptr);
577     }
578     Tfree(arr);
579 }
580 
581 void CMemTest::Zerofilling()
582 {
583     TestStruct* TSMas;
584     size_t CountElement;
585     CountErrors=0;
586     if (FullLog) REPORT("\nzeroings elements of array....");
587     //test struct
588     for (int i=0; i<COUNTEXPERIMENT; i++)
589     {
590         CountElement=rand()%MAX_SIZE;
591         TSMas=(TestStruct*)Tcalloc(CountElement,sizeof(TestStruct));
592         if (NULL == TSMas)
593             continue;
594         for (size_t j=0; j<CountElement; j++)
595         {
596             if (NonZero(TSMas+j, sizeof(TestStruct)))
597             {
598                 CountErrors++;
599                 if (ShouldReportError()) REPORT("detect nonzero element at TestStruct\n");
600             }
601         }
602         Tfree(TSMas);
603     }
604     if (CountErrors) REPORT("%s\n",strError);
605     else if (FullLog) REPORT("%s\n",strOk);
606     error_occurred |= ( CountErrors>0 ) ;
607 }
608 
609 #if !__APPLE__
610 
611 void myMemset(void *ptr, int c, size_t n)
612 {
613 #if  __linux__ &&  __i386__
614 // memset in Fedora 13 not always correctly sets memory to required values.
615     char *p = (char*)ptr;
616     for (size_t i=0; i<n; i++)
617         p[i] = c;
618 #else
619     memset(ptr, c, n);
620 #endif
621 }
622 
623 // This test requires more than TOTAL_MB_ALLOC MB of RAM.
624 #if __ANDROID__
625 // Android requires lower limit due to lack of virtual memory.
626 #define TOTAL_MB_ALLOC	200
627 #else
628 #define TOTAL_MB_ALLOC  800
629 #endif
630 void CMemTest::NULLReturn(UINT MinSize, UINT MaxSize, int total_threads)
631 {
632     const int MB_PER_THREAD = TOTAL_MB_ALLOC / total_threads;
633     // find size to guarantee getting NULL for 1024 B allocations
634     const int MAXNUM_1024 = (MB_PER_THREAD + (MB_PER_THREAD>>2)) * 1024;
635 
636     std::vector<MemStruct> PointerList;
637     void *tmp;
638     CountErrors=0;
639     int CountNULL, num_1024;
640     if (FullLog) REPORT("\nNULL return & check errno:\n");
641     UINT Size;
642     Limit limit_total(TOTAL_MB_ALLOC), no_limit(0);
643     void **buf_1024 = (void**)Tmalloc(MAXNUM_1024*sizeof(void*));
644 
645     REQUIRE(buf_1024);
646     /* We must have space for pointers when memory limit is hit.
647        Reserve enough for the worst case, taking into account race for
648        limited space between threads.
649     */
650     PointerList.reserve(TOTAL_MB_ALLOC*MByte/MinSize);
651 
652     /* There is a bug in the specific version of GLIBC (2.5-12) shipped
653        with RHEL5 that leads to erroneous working of the test
654        on Intel(R) 64 and Itanium(R) architecture when setrlimit-related part is enabled.
655        Switching to GLIBC 2.5-18 from RHEL5.1 resolved the issue.
656      */
657     if (perProcessLimits)
658         limitBarrier->wait(limit_total);
659     else
660         limitMem(MB_PER_THREAD);
661 
662     /* regression test against the bug in allocator when it dereference NULL
663        while lack of memory
664     */
665     for (num_1024=0; num_1024<MAXNUM_1024; num_1024++) {
666         buf_1024[num_1024] = Tcalloc(1024, 1);
667         if (! buf_1024[num_1024]) {
668             ASSERT_ERRNO(errno == ENOMEM, NULL);
669             break;
670         }
671     }
672     for (int i=0; i<num_1024; i++)
673         Tfree(buf_1024[i]);
674     Tfree(buf_1024);
675 
676     do {
677         Size=rand()%(MaxSize-MinSize)+MinSize;
678         tmp=Tmalloc(Size);
679         if (tmp != NULL)
680         {
681             myMemset(tmp, 0, Size);
682             PointerList.push_back(MemStruct(tmp, Size));
683         }
684     } while(tmp != NULL);
685     ASSERT_ERRNO(errno == ENOMEM, NULL);
686     if (FullLog) REPORT("\n");
687 
688     // preparation complete, now running tests
689     // malloc
690     if (FullLog) REPORT("malloc....");
691     CountNULL = 0;
692     while (CountNULL==0)
693         for (int j=0; j<COUNT_TESTS; j++)
694         {
695             Size=rand()%(MaxSize-MinSize)+MinSize;
696             errno = ENOMEM+j+1;
697             tmp=Tmalloc(Size);
698             if (tmp == NULL)
699             {
700                 CountNULL++;
701                 if ( CHECK_ERRNO(errno != ENOMEM) ) {
702                     CountErrors++;
703                     if (ShouldReportError()) REPORT("NULL returned, error: errno (%d) != ENOMEM\n", errno);
704                 }
705             }
706             else
707             {
708                 // Technically, if malloc returns a non-NULL pointer, it is allowed to set errno anyway.
709                 // However, on most systems it does not set errno.
710                 bool known_issue = false;
711 #if __linux__ || __ANDROID__
712                 if( CHECK_ERRNO(errno==ENOMEM) ) known_issue = true;
713 #endif /* __linux__ */
714                 if ( CHECK_ERRNO(errno != ENOMEM+j+1) && !known_issue) {
715                     CountErrors++;
716                     if (ShouldReportError()) REPORT("error: errno changed to %d though valid pointer was returned\n", errno);
717                 }
718                 myMemset(tmp, 0, Size);
719                 PointerList.push_back(MemStruct(tmp, Size));
720             }
721         }
722     if (FullLog) REPORT("end malloc\n");
723     if (CountErrors) REPORT("%s\n",strError);
724     else if (FullLog) REPORT("%s\n",strOk);
725     error_occurred |= ( CountErrors>0 ) ;
726 
727     CountErrors=0;
728     //calloc
729     if (FullLog) REPORT("calloc....");
730     CountNULL = 0;
731     while (CountNULL==0)
732         for (int j=0; j<COUNT_TESTS; j++)
733         {
734             Size=rand()%(MaxSize-MinSize)+MinSize;
735             errno = ENOMEM+j+1;
736             tmp=Tcalloc(COUNT_ELEM_CALLOC,Size);
737             if (tmp == NULL)
738             {
739                 CountNULL++;
740                 if ( CHECK_ERRNO(errno != ENOMEM) ){
741                     CountErrors++;
742                     if (ShouldReportError()) REPORT("NULL returned, error: errno(%d) != ENOMEM\n", errno);
743                 }
744             }
745             else
746             {
747                 // Technically, if calloc returns a non-NULL pointer, it is allowed to set errno anyway.
748                 // However, on most systems it does not set errno.
749                 bool known_issue = false;
750 #if __linux__
751                 if( CHECK_ERRNO(errno==ENOMEM) ) known_issue = true;
752 #endif /* __linux__ */
753                 if ( CHECK_ERRNO(errno != ENOMEM+j+1) && !known_issue ) {
754                     CountErrors++;
755                     if (ShouldReportError()) REPORT("error: errno changed to %d though valid pointer was returned\n", errno);
756                 }
757                 PointerList.push_back(MemStruct(tmp, Size));
758             }
759         }
760     if (FullLog) REPORT("end calloc\n");
761     if (CountErrors) REPORT("%s\n",strError);
762     else if (FullLog) REPORT("%s\n",strOk);
763     error_occurred |= ( CountErrors>0 ) ;
764     CountErrors=0;
765     if (FullLog) REPORT("realloc....");
766     CountNULL = 0;
767     if (PointerList.size() > 0)
768         while (CountNULL==0)
769             for (size_t i=0; i<(size_t)COUNT_TESTS && i<PointerList.size(); i++)
770             {
771                 errno = 0;
772                 tmp=Trealloc(PointerList[i].Pointer,PointerList[i].Size*2);
773                 if (tmp != NULL) // same or another place
774                 {
775                     bool known_issue = false;
776 #if __linux__
777                     if( errno==ENOMEM ) known_issue = true;
778 #endif /* __linux__ */
779                     if (errno != 0 && !known_issue) {
780                         CountErrors++;
781                         if (ShouldReportError()) REPORT("valid pointer returned, error: errno not kept\n");
782                     }
783                     // newly allocated area have to be zeroed
784                     myMemset((char*)tmp + PointerList[i].Size, 0, PointerList[i].Size);
785                     PointerList[i].Pointer = tmp;
786                     PointerList[i].Size *= 2;
787                 } else {
788                     CountNULL++;
789                     if ( CHECK_ERRNO(errno != ENOMEM) )
790                     {
791                         CountErrors++;
792                         if (ShouldReportError()) REPORT("NULL returned, error: errno(%d) != ENOMEM\n", errno);
793                     }
794                     // check data integrity
795                     if (NonZero(PointerList[i].Pointer, PointerList[i].Size)) {
796                         CountErrors++;
797                         if (ShouldReportError()) REPORT("NULL returned, error: data changed\n");
798                     }
799                 }
800             }
801     if (FullLog) REPORT("realloc end\n");
802     if (CountErrors) REPORT("%s\n",strError);
803     else if (FullLog) REPORT("%s\n",strOk);
804     error_occurred |= ( CountErrors>0 ) ;
805     for (UINT i=0; i<PointerList.size(); i++)
806     {
807         Tfree(PointerList[i].Pointer);
808     }
809 
810     if (perProcessLimits)
811         limitBarrier->wait(no_limit);
812     else
813         limitMem(0);
814 }
815 #endif /* #if !__APPLE__ */
816 
817 void CMemTest::UniquePointer()
818 {
819     CountErrors=0;
820     int **MasPointer = (int **)Tmalloc(sizeof(int*)*COUNT_ELEM);
821     size_t *MasCountElem = (size_t*)Tmalloc(sizeof(size_t)*COUNT_ELEM);
822     if (FullLog) REPORT("\nUnique pointer using 0\n");
823     REQUIRE((MasCountElem && MasPointer));
824     //
825     //-------------------------------------------------------
826     //malloc
827     for (int i=0; i<COUNT_ELEM; i++)
828     {
829         MasCountElem[i]=rand()%MAX_SIZE;
830         MasPointer[i]=(int*)Tmalloc(MasCountElem[i]*sizeof(int));
831         if (NULL == MasPointer[i])
832             MasCountElem[i]=0;
833         memset(MasPointer[i], 0, sizeof(int)*MasCountElem[i]);
834     }
835     if (FullLog) REPORT("malloc....");
836     for (UINT i=0; i<COUNT_ELEM-1; i++)
837     {
838         if (size_t badOff = NonZero(MasPointer[i], sizeof(int)*MasCountElem[i])) {
839             CountErrors++;
840             if (ShouldReportError())
841                 REPORT("error, detect non-zero at %p\n", (char*)MasPointer[i]+badOff-1);
842         }
843         memset(MasPointer[i], 1, sizeof(int)*MasCountElem[i]);
844     }
845     if (CountErrors) REPORT("%s\n",strError);
846     else if (FullLog) REPORT("%s\n",strOk);
847     error_occurred |= ( CountErrors>0 ) ;
848     //----------------------------------------------------------
849     //calloc
850     for (int i=0; i<COUNT_ELEM; i++)
851         Tfree(MasPointer[i]);
852     CountErrors=0;
853     for (long i=0; i<COUNT_ELEM; i++)
854     {
855         MasPointer[i]=(int*)Tcalloc(MasCountElem[i]*sizeof(int),2);
856         if (NULL == MasPointer[i])
857             MasCountElem[i]=0;
858     }
859     if (FullLog) REPORT("calloc....");
860     for (int i=0; i<COUNT_ELEM-1; i++)
861     {
862         if (size_t badOff = NonZero(MasPointer[i], sizeof(int)*MasCountElem[i])) {
863             CountErrors++;
864             if (ShouldReportError())
865                 REPORT("error, detect non-zero at %p\n", (char*)MasPointer[i]+badOff-1);
866         }
867         memset(MasPointer[i], 1, sizeof(int)*MasCountElem[i]);
868     }
869     if (CountErrors) REPORT("%s\n",strError);
870     else if (FullLog) REPORT("%s\n",strOk);
871     error_occurred |= ( CountErrors>0 ) ;
872     //---------------------------------------------------------
873     //realloc
874     CountErrors=0;
875     for (int i=0; i<COUNT_ELEM; i++)
876     {
877         MasCountElem[i]*=2;
878         *(MasPointer+i)=
879             (int*)Trealloc(*(MasPointer+i),MasCountElem[i]*sizeof(int));
880         if (NULL == MasPointer[i])
881             MasCountElem[i]=0;
882         memset(MasPointer[i], 0, sizeof(int)*MasCountElem[i]);
883     }
884     if (FullLog) REPORT("realloc....");
885     for (int i=0; i<COUNT_ELEM-1; i++)
886     {
887         if (NonZero(MasPointer[i], sizeof(int)*MasCountElem[i]))
888             CountErrors++;
889         memset(MasPointer[i], 1, sizeof(int)*MasCountElem[i]);
890     }
891     if (CountErrors) REPORT("%s\n",strError);
892     else if (FullLog) REPORT("%s\n",strOk);
893     error_occurred |= ( CountErrors>0 ) ;
894     for (int i=0; i<COUNT_ELEM; i++)
895         Tfree(MasPointer[i]);
896     Tfree(MasCountElem);
897     Tfree(MasPointer);
898 }
899 
900 bool CMemTest::ShouldReportError()
901 {
902     if (FullLog)
903         return true;
904     else
905         if (firstTime) {
906             firstTime = false;
907             return true;
908         } else
909             return false;
910 }
911 
912 void CMemTest::Free_NULL()
913 {
914     CountErrors=0;
915     if (FullLog) REPORT("\ncall free with parameter NULL....");
916     errno = 0;
917     for (int i=0; i<COUNTEXPERIMENT; i++)
918     {
919         Tfree(NULL);
920         if (CHECK_ERRNO(errno))
921         {
922             CountErrors++;
923             if (ShouldReportError()) REPORT("error is found by a call free with parameter NULL\n");
924         }
925     }
926     if (CountErrors) REPORT("%s\n",strError);
927     else if (FullLog) REPORT("%s\n",strOk);
928     error_occurred |= ( CountErrors>0 ) ;
929 }
930 
931 void CMemTest::TestAlignedParameters()
932 {
933     void *memptr;
934     int ret;
935 
936     if (Rposix_memalign) {
937         // alignment isn't power of 2
938         for (int bad_align=3; bad_align<16; bad_align++)
939             if (bad_align&(bad_align-1)) {
940                 ret = Tposix_memalign(NULL, bad_align, 100);
941                 REQUIRE(EINVAL==ret);
942             }
943 
944         memptr = &ret;
945         ret = Tposix_memalign(&memptr, 5*sizeof(void*), 100);
946         REQUIRE_MESSAGE(memptr == &ret,
947                "memptr should not be changed after unsuccessful call");
948         REQUIRE(EINVAL==ret);
949 
950         // alignment is power of 2, but not a multiple of sizeof(void *),
951         // we expect that sizeof(void*) > 2
952         ret = Tposix_memalign(NULL, 2, 100);
953         REQUIRE(EINVAL==ret);
954     }
955     if (Raligned_malloc) {
956         // alignment isn't power of 2
957         for (int bad_align=3; bad_align<16; bad_align++)
958             if (bad_align&(bad_align-1)) {
959                 memptr = Taligned_malloc(100, bad_align);
960                 REQUIRE(memptr == nullptr);
961                 ASSERT_ERRNO(EINVAL==errno, NULL);
962             }
963 
964         // size is zero
965         memptr = Taligned_malloc(0, 16);
966         REQUIRE_MESSAGE(memptr == nullptr, "size is zero, so must return NULL");
967         ASSERT_ERRNO(EINVAL==errno, NULL);
968     }
969     if (Taligned_free) {
970         // NULL pointer is OK to free
971         errno = 0;
972         Taligned_free(NULL);
973         /* As there is no return value for free, strictly speaking we can't
974            check errno here. But checked implementations obey the assertion.
975         */
976         ASSERT_ERRNO(0==errno, NULL);
977     }
978     if (Raligned_realloc) {
979         for (int i=1; i<20; i++) {
980             // checks that calls work correctly in presence of non-zero errno
981             errno = i;
982             void *ptr = Taligned_malloc(i*10, 128);
983             REQUIRE(ptr != nullptr);
984             ASSERT_ERRNO(0!=errno, NULL);
985             // if size is zero and pointer is not NULL, works like free
986             memptr = Taligned_realloc(ptr, 0, 64);
987             REQUIRE(memptr == nullptr);
988             ASSERT_ERRNO(0!=errno, NULL);
989         }
990         // alignment isn't power of 2
991         for (int bad_align=3; bad_align<16; bad_align++)
992             if (bad_align&(bad_align-1)) {
993                 void *ptr = &bad_align;
994                 memptr = Taligned_realloc(&ptr, 100, bad_align);
995                 REQUIRE(memptr == nullptr);
996                 REQUIRE(&bad_align==ptr);
997                 ASSERT_ERRNO(EINVAL==errno, NULL);
998             }
999     }
1000 }
1001 
1002 void CMemTest::RunAllTests(int total_threads)
1003 {
1004     Zerofilling();
1005     Free_NULL();
1006     InvariantDataRealloc(/*aligned=*/false, 8*MByte, /*checkData=*/true);
1007     if (Raligned_realloc)
1008         InvariantDataRealloc(/*aligned=*/true, 8*MByte, /*checkData=*/true);
1009     TestAlignedParameters();
1010     UniquePointer();
1011     AddrArifm();
1012 #if __APPLE__
1013     REPORT("Known issue: some tests are skipped on macOS\n");
1014 #else
1015     NULLReturn(1*MByte,100*MByte,total_threads);
1016 #endif
1017     if (FullLog) REPORT("Tests for %d threads ended\n", total_threads);
1018 }
1019 
1020 // TODO: fix the tests to support UWP mode
1021 #if !__TBB_WIN8UI_SUPPORT
1022 
1023 TEST_CASE("MAIN TEST") {
1024     Rmalloc=scalable_malloc;
1025     Rrealloc=scalable_realloc;
1026     Rcalloc=scalable_calloc;
1027     Tfree=scalable_free;
1028     Rposix_memalign=scalable_posix_memalign;
1029     Raligned_malloc=scalable_aligned_malloc;
1030     Raligned_realloc=scalable_aligned_realloc;
1031     Taligned_free=scalable_aligned_free;
1032 
1033     // Check if we were called to test standard behavior
1034     // TODO: enable this mode
1035     // setSystemAllocs();
1036 
1037 #if __linux__
1038     /* According to man pthreads
1039        "NPTL threads do not share resource limits (fixed in kernel 2.6.10)".
1040        Use per-threads limits for affected systems.
1041      */
1042     if ( utils::LinuxKernelVersion() < 2*1000000 + 6*1000 + 10)
1043         perProcessLimits = false;
1044 #endif
1045     //-------------------------------------
1046 #if __APPLE__
1047     /* Skip due to lack of memory limit enforcing under macOS. */
1048 #else
1049     limitMem(200);
1050     ReallocParam();
1051     limitMem(0);
1052 #endif
1053 
1054 //for linux and dynamic runtime errno is used to check allocator functions
1055 //check if library compiled with /MD(d) and we can use errno
1056 #if _MSC_VER
1057 #if defined(_MT) && defined(_DLL) //check errno if test itself compiled with /MD(d) only
1058     char*  version_info_block = NULL;
1059     int version_info_block_size;
1060     LPVOID comments_block = NULL;
1061     UINT comments_block_size;
1062 #ifdef _DEBUG
1063 #define __TBBMALLOCDLL "tbbmalloc_debug.dll"
1064 #else  //_DEBUG
1065 #define __TBBMALLOCDLL "tbbmalloc.dll"
1066 #endif //_DEBUG
1067     version_info_block_size = GetFileVersionInfoSize( __TBBMALLOCDLL, (LPDWORD)&version_info_block_size );
1068     if( version_info_block_size
1069         && ((version_info_block = (char*)malloc(version_info_block_size)) != NULL)
1070         && GetFileVersionInfo(  __TBBMALLOCDLL, NULL, version_info_block_size, version_info_block )
1071         && VerQueryValue( version_info_block, "\\StringFileInfo\\000004b0\\Comments", &comments_block, &comments_block_size )
1072         && strstr( (char*)comments_block, "/MD" )
1073         ){
1074             __tbb_test_errno = true;
1075      }
1076      if( version_info_block ) free( version_info_block );
1077 #endif // defined(_MT) && defined(_DLL)
1078 #else  // _MSC_VER
1079     __tbb_test_errno = true;
1080 #endif // _MSC_VER
1081 
1082     CheckArgumentsOverflow();
1083     CheckReallocLeak();
1084     for( int p=MaxThread; p>=MinThread; --p ) {
1085         for (int limit=0; limit<2; limit++) {
1086             int ret = scalable_allocation_mode(TBBMALLOC_SET_SOFT_HEAP_LIMIT, 16*1024*limit);
1087             REQUIRE(ret==TBBMALLOC_OK);
1088             utils::SpinBarrier *barrier = new utils::SpinBarrier(p);
1089             utils::NativeParallelFor( p, RoundRobin(p, barrier, Verbose) );
1090             delete barrier;
1091         }
1092     }
1093     int ret = scalable_allocation_mode(TBBMALLOC_SET_SOFT_HEAP_LIMIT, 0);
1094     REQUIRE(ret==TBBMALLOC_OK);
1095     REQUIRE(!error_occurred);
1096 }
1097 
1098 #endif /* __TBB_WIN8UI_SUPPORT	 */
1099