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