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