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