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 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 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 190 MemStruct() : Pointer(nullptr), Size(0) {} 191 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: 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); 214 ~CMemTest() {} 215 }; 216 217 class Limit { 218 size_t limit; 219 public: 220 Limit(size_t a_limit) : limit(a_limit) {} 221 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 233 RoundRobin( long p, utils::SpinBarrier *limitBarrier, bool verbose ) : 234 number_of_threads(p), test(limitBarrier, verbose) {} 235 void operator()( int /*id*/ ) const 236 { 237 test.RunAllTests(number_of_threads); 238 } 239 }; 240 241 bool CMemTest::firstTime = true; 242 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 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 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 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 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 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 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 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 } 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 } 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 } 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 } 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 } 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 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 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 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 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 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 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 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 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 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 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