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