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