151c0b2f7Stbbdev /*
251c0b2f7Stbbdev     Copyright (c) 2005-2020 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev //! \file test_malloc_whitebox.cpp
1851c0b2f7Stbbdev //! \brief Test for [memory_allocation] functionality
1951c0b2f7Stbbdev 
2051c0b2f7Stbbdev // To prevent loading dynamic TBBmalloc at startup, that is not needed for the whitebox test
2151c0b2f7Stbbdev #define __TBB_SOURCE_DIRECTLY_INCLUDED 1
2251c0b2f7Stbbdev // Call thread shutdown API for native threads join
2351c0b2f7Stbbdev #define HARNESS_TBBMALLOC_THREAD_SHUTDOWN 1
2451c0b2f7Stbbdev 
2551c0b2f7Stbbdev // According to C99 standard INTPTR_MIN defined for C++ if __STDC_LIMIT_MACROS pre-defined
2651c0b2f7Stbbdev #define __STDC_LIMIT_MACROS 1
2751c0b2f7Stbbdev 
2851c0b2f7Stbbdev // To not depends on ITT support stuff
2951c0b2f7Stbbdev #ifdef DO_ITT_NOTIFY
3051c0b2f7Stbbdev #undef DO_ITT_NOTIFY
3151c0b2f7Stbbdev #endif
3251c0b2f7Stbbdev 
3351c0b2f7Stbbdev #include "common/test.h"
3451c0b2f7Stbbdev 
3551c0b2f7Stbbdev #include "common/utils.h"
3651c0b2f7Stbbdev #include "common/utils_assert.h"
3751c0b2f7Stbbdev #include "common/utils_env.h"
3851c0b2f7Stbbdev #include "common/spin_barrier.h"
3951c0b2f7Stbbdev 
40*49e08aacStbbdev #include "oneapi/tbb/detail/_machine.h"
4151c0b2f7Stbbdev 
4251c0b2f7Stbbdev #define __TBB_MALLOC_WHITEBOX_TEST 1 // to get access to allocator internals
4351c0b2f7Stbbdev // help trigger rare race condition
4451c0b2f7Stbbdev #define WhiteboxTestingYield() (tbb::detail::yield(), tbb::detail::yield(), tbb::detail::yield(), tbb::detail::yield())
4551c0b2f7Stbbdev 
4651c0b2f7Stbbdev #if __INTEL_COMPILER && __TBB_MIC_OFFLOAD
4751c0b2f7Stbbdev // 2571 is variable has not been declared with compatible "target" attribute
4851c0b2f7Stbbdev // 3218 is class/struct may fail when offloaded because this field is misaligned
4951c0b2f7Stbbdev //         or contains data that is misaligned
5051c0b2f7Stbbdev     #pragma warning(push)
5151c0b2f7Stbbdev     #pragma warning(disable:2571 3218)
5251c0b2f7Stbbdev #endif
5351c0b2f7Stbbdev #define protected public
5451c0b2f7Stbbdev #define private public
5551c0b2f7Stbbdev #include "../../src/tbbmalloc/frontend.cpp"
5651c0b2f7Stbbdev #undef protected
5751c0b2f7Stbbdev #undef private
5851c0b2f7Stbbdev #if __INTEL_COMPILER && __TBB_MIC_OFFLOAD
5951c0b2f7Stbbdev     #pragma warning(pop)
6051c0b2f7Stbbdev #endif
6151c0b2f7Stbbdev #include "../../src/tbbmalloc/backend.cpp"
6251c0b2f7Stbbdev #include "../../src/tbbmalloc/backref.cpp"
6351c0b2f7Stbbdev 
6451c0b2f7Stbbdev namespace tbbmalloc_whitebox {
6551c0b2f7Stbbdev     size_t locGetProcessed = 0;
6651c0b2f7Stbbdev     size_t locPutProcessed = 0;
6751c0b2f7Stbbdev }
6851c0b2f7Stbbdev #include "../../src/tbbmalloc/large_objects.cpp"
6951c0b2f7Stbbdev #include "../../src/tbbmalloc/tbbmalloc.cpp"
7051c0b2f7Stbbdev 
7151c0b2f7Stbbdev const int LARGE_MEM_SIZES_NUM = 10;
7251c0b2f7Stbbdev static const int MinThread = 1;
7351c0b2f7Stbbdev static const int MaxThread = 4;
7451c0b2f7Stbbdev 
7551c0b2f7Stbbdev class AllocInfo {
7651c0b2f7Stbbdev     int *p;
7751c0b2f7Stbbdev     int val;
7851c0b2f7Stbbdev     int size;
7951c0b2f7Stbbdev public:
8051c0b2f7Stbbdev     AllocInfo() : p(NULL), val(0), size(0) {}
8151c0b2f7Stbbdev     explicit AllocInfo(int sz) : p((int*)scalable_malloc(sz*sizeof(int))),
8251c0b2f7Stbbdev                                    val(rand()), size(sz) {
8351c0b2f7Stbbdev         REQUIRE(p);
8451c0b2f7Stbbdev         for (int k=0; k<size; k++)
8551c0b2f7Stbbdev             p[k] = val;
8651c0b2f7Stbbdev     }
8751c0b2f7Stbbdev     void check() const {
8851c0b2f7Stbbdev         for (int k=0; k<size; k++)
8951c0b2f7Stbbdev             ASSERT(p[k] == val, NULL);
9051c0b2f7Stbbdev     }
9151c0b2f7Stbbdev     void clear() {
9251c0b2f7Stbbdev         scalable_free(p);
9351c0b2f7Stbbdev     }
9451c0b2f7Stbbdev };
9551c0b2f7Stbbdev 
9651c0b2f7Stbbdev class SimpleBarrier: utils::NoAssign {
9751c0b2f7Stbbdev protected:
9851c0b2f7Stbbdev     static utils::SpinBarrier barrier;
9951c0b2f7Stbbdev public:
10051c0b2f7Stbbdev     static void initBarrier(unsigned thrds) { barrier.initialize(thrds); }
10151c0b2f7Stbbdev };
10251c0b2f7Stbbdev 
10351c0b2f7Stbbdev utils::SpinBarrier SimpleBarrier::barrier;
10451c0b2f7Stbbdev 
10551c0b2f7Stbbdev class TestLargeObjCache: public SimpleBarrier {
10651c0b2f7Stbbdev public:
10751c0b2f7Stbbdev     static int largeMemSizes[LARGE_MEM_SIZES_NUM];
10851c0b2f7Stbbdev 
10951c0b2f7Stbbdev     TestLargeObjCache( ) {}
11051c0b2f7Stbbdev 
11151c0b2f7Stbbdev     void operator()( int /*mynum*/ ) const {
11251c0b2f7Stbbdev         AllocInfo allocs[LARGE_MEM_SIZES_NUM];
11351c0b2f7Stbbdev 
11451c0b2f7Stbbdev         // push to maximal cache limit
11551c0b2f7Stbbdev         for (int i=0; i<2; i++) {
11651c0b2f7Stbbdev             const int sizes[] = { MByte/sizeof(int),
11751c0b2f7Stbbdev                                   (MByte-2*LargeObjectCache::LargeBSProps::CacheStep)/sizeof(int) };
11851c0b2f7Stbbdev             for (int q=0; q<2; q++) {
11951c0b2f7Stbbdev                 size_t curr = 0;
12051c0b2f7Stbbdev                 for (int j=0; j<LARGE_MEM_SIZES_NUM; j++, curr++)
12151c0b2f7Stbbdev                     new (allocs+curr) AllocInfo(sizes[q]);
12251c0b2f7Stbbdev 
12351c0b2f7Stbbdev                 for (size_t j=0; j<curr; j++) {
12451c0b2f7Stbbdev                     allocs[j].check();
12551c0b2f7Stbbdev                     allocs[j].clear();
12651c0b2f7Stbbdev                 }
12751c0b2f7Stbbdev             }
12851c0b2f7Stbbdev         }
12951c0b2f7Stbbdev 
13051c0b2f7Stbbdev         barrier.wait();
13151c0b2f7Stbbdev 
13251c0b2f7Stbbdev         // check caching correctness
13351c0b2f7Stbbdev         for (int i=0; i<1000; i++) {
13451c0b2f7Stbbdev             size_t curr = 0;
13551c0b2f7Stbbdev             for (int j=0; j<LARGE_MEM_SIZES_NUM-1; j++, curr++)
13651c0b2f7Stbbdev                 new (allocs+curr) AllocInfo(largeMemSizes[j]);
13751c0b2f7Stbbdev 
13851c0b2f7Stbbdev             new (allocs+curr)
13951c0b2f7Stbbdev                 AllocInfo((int)(4*minLargeObjectSize +
14051c0b2f7Stbbdev                                 2*minLargeObjectSize*(1.*rand()/RAND_MAX)));
14151c0b2f7Stbbdev             curr++;
14251c0b2f7Stbbdev 
14351c0b2f7Stbbdev             for (size_t j=0; j<curr; j++) {
14451c0b2f7Stbbdev                 allocs[j].check();
14551c0b2f7Stbbdev                 allocs[j].clear();
14651c0b2f7Stbbdev             }
14751c0b2f7Stbbdev         }
14851c0b2f7Stbbdev     }
14951c0b2f7Stbbdev };
15051c0b2f7Stbbdev 
15151c0b2f7Stbbdev int TestLargeObjCache::largeMemSizes[LARGE_MEM_SIZES_NUM];
15251c0b2f7Stbbdev 
15351c0b2f7Stbbdev void TestLargeObjectCache()
15451c0b2f7Stbbdev {
15551c0b2f7Stbbdev     for (int i=0; i<LARGE_MEM_SIZES_NUM; i++)
15651c0b2f7Stbbdev         TestLargeObjCache::largeMemSizes[i] =
15751c0b2f7Stbbdev             (int)(minLargeObjectSize + 2*minLargeObjectSize*(1.*rand()/RAND_MAX));
15851c0b2f7Stbbdev 
15951c0b2f7Stbbdev     for( int p=MaxThread; p>=MinThread; --p ) {
16051c0b2f7Stbbdev         TestLargeObjCache::initBarrier( p );
16151c0b2f7Stbbdev         utils::NativeParallelFor( p, TestLargeObjCache() );
16251c0b2f7Stbbdev     }
16351c0b2f7Stbbdev }
16451c0b2f7Stbbdev 
16551c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION
16651c0b2f7Stbbdev 
16751c0b2f7Stbbdev class TestStartupAlloc: public SimpleBarrier {
16851c0b2f7Stbbdev     struct TestBlock {
16951c0b2f7Stbbdev         void *ptr;
17051c0b2f7Stbbdev         size_t sz;
17151c0b2f7Stbbdev     };
17251c0b2f7Stbbdev     static const int ITERS = 100;
17351c0b2f7Stbbdev public:
17451c0b2f7Stbbdev     TestStartupAlloc() {}
17551c0b2f7Stbbdev     void operator()(int) const {
17651c0b2f7Stbbdev         TestBlock blocks1[ITERS], blocks2[ITERS];
17751c0b2f7Stbbdev 
17851c0b2f7Stbbdev         barrier.wait();
17951c0b2f7Stbbdev 
18051c0b2f7Stbbdev         for (int i=0; i<ITERS; i++) {
18151c0b2f7Stbbdev             blocks1[i].sz = rand() % minLargeObjectSize;
18251c0b2f7Stbbdev             blocks1[i].ptr = StartupBlock::allocate(blocks1[i].sz);
18351c0b2f7Stbbdev             REQUIRE((blocks1[i].ptr && StartupBlock::msize(blocks1[i].ptr)>=blocks1[i].sz
18451c0b2f7Stbbdev                    && 0==(uintptr_t)blocks1[i].ptr % sizeof(void*)));
18551c0b2f7Stbbdev             memset(blocks1[i].ptr, i, blocks1[i].sz);
18651c0b2f7Stbbdev         }
18751c0b2f7Stbbdev         for (int i=0; i<ITERS; i++) {
18851c0b2f7Stbbdev             blocks2[i].sz = rand() % minLargeObjectSize;
18951c0b2f7Stbbdev             blocks2[i].ptr = StartupBlock::allocate(blocks2[i].sz);
19051c0b2f7Stbbdev             REQUIRE((blocks2[i].ptr && StartupBlock::msize(blocks2[i].ptr)>=blocks2[i].sz
19151c0b2f7Stbbdev                    && 0==(uintptr_t)blocks2[i].ptr % sizeof(void*)));
19251c0b2f7Stbbdev             memset(blocks2[i].ptr, i, blocks2[i].sz);
19351c0b2f7Stbbdev 
19451c0b2f7Stbbdev             for (size_t j=0; j<blocks1[i].sz; j++)
19551c0b2f7Stbbdev                 REQUIRE(*((char*)blocks1[i].ptr+j) == i);
19651c0b2f7Stbbdev             Block *block = (Block *)alignDown(blocks1[i].ptr, slabSize);
19751c0b2f7Stbbdev             ((StartupBlock *)block)->free(blocks1[i].ptr);
19851c0b2f7Stbbdev         }
19951c0b2f7Stbbdev         for (int i=ITERS-1; i>=0; i--) {
20051c0b2f7Stbbdev             for (size_t j=0; j<blocks2[i].sz; j++)
20151c0b2f7Stbbdev                 REQUIRE(*((char*)blocks2[i].ptr+j) == i);
20251c0b2f7Stbbdev             Block *block = (Block *)alignDown(blocks2[i].ptr, slabSize);
20351c0b2f7Stbbdev             ((StartupBlock *)block)->free(blocks2[i].ptr);
20451c0b2f7Stbbdev         }
20551c0b2f7Stbbdev     }
20651c0b2f7Stbbdev };
20751c0b2f7Stbbdev 
20851c0b2f7Stbbdev #endif /* MALLOC_CHECK_RECURSION */
20951c0b2f7Stbbdev 
21051c0b2f7Stbbdev #include <deque>
21151c0b2f7Stbbdev 
21251c0b2f7Stbbdev template<int ITERS>
21351c0b2f7Stbbdev class BackRefWork: utils::NoAssign {
21451c0b2f7Stbbdev     struct TestBlock {
21551c0b2f7Stbbdev         BackRefIdx idx;
21651c0b2f7Stbbdev         char       data;
21751c0b2f7Stbbdev         TestBlock(BackRefIdx idx_) : idx(idx_) {}
21851c0b2f7Stbbdev     };
21951c0b2f7Stbbdev public:
22051c0b2f7Stbbdev     BackRefWork() {}
22151c0b2f7Stbbdev     void operator()(int) const {
22251c0b2f7Stbbdev         size_t cnt;
22351c0b2f7Stbbdev         // it's important to not invalidate pointers to the contents of the container
22451c0b2f7Stbbdev         std::deque<TestBlock> blocks;
22551c0b2f7Stbbdev 
22651c0b2f7Stbbdev         // for ITERS==0 consume all available backrefs
22751c0b2f7Stbbdev         for (cnt=0; !ITERS || cnt<ITERS; cnt++) {
22851c0b2f7Stbbdev             BackRefIdx idx = BackRefIdx::newBackRef(/*largeObj=*/false);
22951c0b2f7Stbbdev             if (idx.isInvalid())
23051c0b2f7Stbbdev                 break;
23151c0b2f7Stbbdev             blocks.push_back(TestBlock(idx));
23251c0b2f7Stbbdev             setBackRef(blocks.back().idx, &blocks.back().data);
23351c0b2f7Stbbdev         }
23451c0b2f7Stbbdev         for (size_t i=0; i<cnt; i++)
23551c0b2f7Stbbdev             REQUIRE((Block*)&blocks[i].data == getBackRef(blocks[i].idx));
23651c0b2f7Stbbdev         for (size_t i=cnt; i>0; i--)
23751c0b2f7Stbbdev             removeBackRef(blocks[i-1].idx);
23851c0b2f7Stbbdev     }
23951c0b2f7Stbbdev };
24051c0b2f7Stbbdev 
24151c0b2f7Stbbdev class LocalCachesHit: utils::NoAssign {
24251c0b2f7Stbbdev     // set ITERS to trigger possible leak of backreferences
24351c0b2f7Stbbdev     // during cleanup on cache overflow and on thread termination
24451c0b2f7Stbbdev     static const int ITERS = 2*(FreeBlockPool::POOL_HIGH_MARK +
24551c0b2f7Stbbdev                                 LocalLOC::LOC_HIGH_MARK);
24651c0b2f7Stbbdev public:
24751c0b2f7Stbbdev     LocalCachesHit() {}
24851c0b2f7Stbbdev     void operator()(int) const {
24951c0b2f7Stbbdev         void *objsSmall[ITERS], *objsLarge[ITERS];
25051c0b2f7Stbbdev 
25151c0b2f7Stbbdev         for (int i=0; i<ITERS; i++) {
25251c0b2f7Stbbdev             objsSmall[i] = scalable_malloc(minLargeObjectSize-1);
25351c0b2f7Stbbdev             objsLarge[i] = scalable_malloc(minLargeObjectSize);
25451c0b2f7Stbbdev         }
25551c0b2f7Stbbdev         for (int i=0; i<ITERS; i++) {
25651c0b2f7Stbbdev             scalable_free(objsSmall[i]);
25751c0b2f7Stbbdev             scalable_free(objsLarge[i]);
25851c0b2f7Stbbdev         }
25951c0b2f7Stbbdev     }
26051c0b2f7Stbbdev };
26151c0b2f7Stbbdev 
26251c0b2f7Stbbdev static size_t allocatedBackRefCount()
26351c0b2f7Stbbdev {
26451c0b2f7Stbbdev     size_t cnt = 0;
26551c0b2f7Stbbdev     for (int i=0; i<=backRefMaster.load(std::memory_order_relaxed)->lastUsed.load(std::memory_order_relaxed); i++)
26651c0b2f7Stbbdev         cnt += backRefMaster.load(std::memory_order_relaxed)->backRefBl[i]->allocatedCount;
26751c0b2f7Stbbdev     return cnt;
26851c0b2f7Stbbdev }
26951c0b2f7Stbbdev 
27051c0b2f7Stbbdev class TestInvalidBackrefs: public SimpleBarrier {
27151c0b2f7Stbbdev #if __ANDROID__
27251c0b2f7Stbbdev     // Android requires lower iters due to lack of virtual memory.
27351c0b2f7Stbbdev     static const int BACKREF_GROWTH_ITERS = 50*1024;
27451c0b2f7Stbbdev #else
27551c0b2f7Stbbdev     static const int BACKREF_GROWTH_ITERS = 200*1024;
27651c0b2f7Stbbdev #endif
27751c0b2f7Stbbdev 
27851c0b2f7Stbbdev     static std::atomic<bool> backrefGrowthDone;
27951c0b2f7Stbbdev     static void *ptrs[BACKREF_GROWTH_ITERS];
28051c0b2f7Stbbdev public:
28151c0b2f7Stbbdev     TestInvalidBackrefs() {}
28251c0b2f7Stbbdev     void operator()(int id) const {
28351c0b2f7Stbbdev 
28451c0b2f7Stbbdev         if (!id) {
28551c0b2f7Stbbdev             backrefGrowthDone = false;
28651c0b2f7Stbbdev             barrier.wait();
28751c0b2f7Stbbdev 
28851c0b2f7Stbbdev             for (int i=0; i<BACKREF_GROWTH_ITERS; i++)
28951c0b2f7Stbbdev                 ptrs[i] = scalable_malloc(minLargeObjectSize);
29051c0b2f7Stbbdev             backrefGrowthDone = true;
29151c0b2f7Stbbdev             for (int i=0; i<BACKREF_GROWTH_ITERS; i++)
29251c0b2f7Stbbdev                 scalable_free(ptrs[i]);
29351c0b2f7Stbbdev         } else {
29451c0b2f7Stbbdev             void *p2 = scalable_malloc(minLargeObjectSize-1);
29551c0b2f7Stbbdev             char *p1 = (char*)scalable_malloc(minLargeObjectSize-1);
29651c0b2f7Stbbdev             LargeObjectHdr *hdr =
29751c0b2f7Stbbdev                 (LargeObjectHdr*)(p1+minLargeObjectSize-1 - sizeof(LargeObjectHdr));
29851c0b2f7Stbbdev             hdr->backRefIdx.master = 7;
29951c0b2f7Stbbdev             hdr->backRefIdx.largeObj = 1;
30051c0b2f7Stbbdev             hdr->backRefIdx.offset = 2000;
30151c0b2f7Stbbdev 
30251c0b2f7Stbbdev             barrier.wait();
30351c0b2f7Stbbdev 
30451c0b2f7Stbbdev             while (!backrefGrowthDone) {
30551c0b2f7Stbbdev                 scalable_free(p2);
30651c0b2f7Stbbdev                 p2 = scalable_malloc(minLargeObjectSize-1);
30751c0b2f7Stbbdev             }
30851c0b2f7Stbbdev             scalable_free(p1);
30951c0b2f7Stbbdev             scalable_free(p2);
31051c0b2f7Stbbdev         }
31151c0b2f7Stbbdev     }
31251c0b2f7Stbbdev };
31351c0b2f7Stbbdev 
31451c0b2f7Stbbdev std::atomic<bool> TestInvalidBackrefs::backrefGrowthDone;
31551c0b2f7Stbbdev void *TestInvalidBackrefs::ptrs[BACKREF_GROWTH_ITERS];
31651c0b2f7Stbbdev 
31751c0b2f7Stbbdev void TestBackRef() {
31851c0b2f7Stbbdev     size_t beforeNumBackRef, afterNumBackRef;
31951c0b2f7Stbbdev 
32051c0b2f7Stbbdev     beforeNumBackRef = allocatedBackRefCount();
32151c0b2f7Stbbdev     for( int p=MaxThread; p>=MinThread; --p )
32251c0b2f7Stbbdev         utils::NativeParallelFor( p, BackRefWork<2*BR_MAX_CNT+2>() );
32351c0b2f7Stbbdev     afterNumBackRef = allocatedBackRefCount();
32451c0b2f7Stbbdev     REQUIRE_MESSAGE(beforeNumBackRef==afterNumBackRef, "backreference leak detected");
32551c0b2f7Stbbdev     // lastUsed marks peak resource consumption. As we allocate below the mark,
32651c0b2f7Stbbdev     // it must not move up, otherwise there is a resource leak.
32751c0b2f7Stbbdev     int sustLastUsed = backRefMaster.load(std::memory_order_relaxed)->lastUsed.load(std::memory_order_relaxed);
32851c0b2f7Stbbdev     utils::NativeParallelFor( 1, BackRefWork<2*BR_MAX_CNT+2>() );
32951c0b2f7Stbbdev     REQUIRE_MESSAGE(sustLastUsed == backRefMaster.load(std::memory_order_relaxed)->lastUsed.load(std::memory_order_relaxed), "backreference leak detected");
33051c0b2f7Stbbdev     // check leak of back references while per-thread caches are in use
33151c0b2f7Stbbdev     // warm up needed to cover bootStrapMalloc call
33251c0b2f7Stbbdev     utils::NativeParallelFor( 1, LocalCachesHit() );
33351c0b2f7Stbbdev     beforeNumBackRef = allocatedBackRefCount();
33451c0b2f7Stbbdev     utils::NativeParallelFor( 2, LocalCachesHit() );
33551c0b2f7Stbbdev     int res = scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS, NULL);
33651c0b2f7Stbbdev     REQUIRE(res == TBBMALLOC_OK);
33751c0b2f7Stbbdev     afterNumBackRef = allocatedBackRefCount();
33851c0b2f7Stbbdev     REQUIRE_MESSAGE(beforeNumBackRef>=afterNumBackRef, "backreference leak detected");
33951c0b2f7Stbbdev 
34051c0b2f7Stbbdev     // This is a regression test against race condition between backreference
34151c0b2f7Stbbdev     // extension and checking invalid BackRefIdx.
34251c0b2f7Stbbdev     // While detecting is object large or small, scalable_free 1st check for
34351c0b2f7Stbbdev     // large objects, so there is a chance to prepend small object with
34451c0b2f7Stbbdev     // seems valid BackRefIdx for large objects, and thus trigger the bug.
34551c0b2f7Stbbdev     TestInvalidBackrefs::initBarrier(MaxThread);
34651c0b2f7Stbbdev     utils::NativeParallelFor( MaxThread, TestInvalidBackrefs() );
34751c0b2f7Stbbdev     // Consume all available backrefs and check they work correctly.
34851c0b2f7Stbbdev     // For now test 32-bit machines only, because for 64-bit memory consumption is too high.
34951c0b2f7Stbbdev     if (sizeof(uintptr_t) == 4)
35051c0b2f7Stbbdev         utils::NativeParallelFor( MaxThread, BackRefWork<0>() );
35151c0b2f7Stbbdev }
35251c0b2f7Stbbdev 
35351c0b2f7Stbbdev void *getMem(intptr_t /*pool_id*/, size_t &bytes)
35451c0b2f7Stbbdev {
35551c0b2f7Stbbdev     const size_t BUF_SIZE = 8*1024*1024;
35651c0b2f7Stbbdev     static char space[BUF_SIZE];
35751c0b2f7Stbbdev     static size_t pos;
35851c0b2f7Stbbdev 
35951c0b2f7Stbbdev     if (pos + bytes > BUF_SIZE)
36051c0b2f7Stbbdev         return NULL;
36151c0b2f7Stbbdev 
36251c0b2f7Stbbdev     void *ret = space + pos;
36351c0b2f7Stbbdev     pos += bytes;
36451c0b2f7Stbbdev 
36551c0b2f7Stbbdev     return ret;
36651c0b2f7Stbbdev }
36751c0b2f7Stbbdev 
36851c0b2f7Stbbdev int putMem(intptr_t /*pool_id*/, void* /*raw_ptr*/, size_t /*raw_bytes*/)
36951c0b2f7Stbbdev {
37051c0b2f7Stbbdev     return 0;
37151c0b2f7Stbbdev }
37251c0b2f7Stbbdev 
37351c0b2f7Stbbdev struct MallocPoolHeader {
37451c0b2f7Stbbdev     void  *rawPtr;
37551c0b2f7Stbbdev     size_t userSize;
37651c0b2f7Stbbdev };
37751c0b2f7Stbbdev 
37851c0b2f7Stbbdev void *getMallocMem(intptr_t /*pool_id*/, size_t &bytes)
37951c0b2f7Stbbdev {
38051c0b2f7Stbbdev     void *rawPtr = malloc(bytes+sizeof(MallocPoolHeader));
38151c0b2f7Stbbdev     void *ret = (void *)((uintptr_t)rawPtr+sizeof(MallocPoolHeader));
38251c0b2f7Stbbdev 
38351c0b2f7Stbbdev     MallocPoolHeader *hdr = (MallocPoolHeader*)ret-1;
38451c0b2f7Stbbdev     hdr->rawPtr = rawPtr;
38551c0b2f7Stbbdev     hdr->userSize = bytes;
38651c0b2f7Stbbdev 
38751c0b2f7Stbbdev     return ret;
38851c0b2f7Stbbdev }
38951c0b2f7Stbbdev 
39051c0b2f7Stbbdev int putMallocMem(intptr_t /*pool_id*/, void *ptr, size_t bytes)
39151c0b2f7Stbbdev {
39251c0b2f7Stbbdev     MallocPoolHeader *hdr = (MallocPoolHeader*)ptr-1;
39351c0b2f7Stbbdev     ASSERT(bytes == hdr->userSize, "Invalid size in pool callback.");
39451c0b2f7Stbbdev     free(hdr->rawPtr);
39551c0b2f7Stbbdev 
39651c0b2f7Stbbdev     return 0;
39751c0b2f7Stbbdev }
39851c0b2f7Stbbdev 
39951c0b2f7Stbbdev class StressLOCacheWork: utils::NoAssign {
40051c0b2f7Stbbdev     rml::MemoryPool *my_mallocPool;
40151c0b2f7Stbbdev public:
40251c0b2f7Stbbdev     StressLOCacheWork(rml::MemoryPool *mallocPool) : my_mallocPool(mallocPool) {}
40351c0b2f7Stbbdev     void operator()(int) const {
40451c0b2f7Stbbdev         for (size_t sz=minLargeObjectSize; sz<1*1024*1024;
40551c0b2f7Stbbdev              sz+=LargeObjectCache::LargeBSProps::CacheStep) {
40651c0b2f7Stbbdev             void *ptr = pool_malloc(my_mallocPool, sz);
40751c0b2f7Stbbdev             REQUIRE_MESSAGE(ptr, "Memory was not allocated");
40851c0b2f7Stbbdev             memset(ptr, sz, sz);
40951c0b2f7Stbbdev             pool_free(my_mallocPool, ptr);
41051c0b2f7Stbbdev         }
41151c0b2f7Stbbdev     }
41251c0b2f7Stbbdev };
41351c0b2f7Stbbdev 
41451c0b2f7Stbbdev void TestPools() {
41551c0b2f7Stbbdev     rml::MemPoolPolicy pol(getMem, putMem);
41651c0b2f7Stbbdev     size_t beforeNumBackRef, afterNumBackRef;
41751c0b2f7Stbbdev 
41851c0b2f7Stbbdev     rml::MemoryPool *pool1;
41951c0b2f7Stbbdev     rml::MemoryPool *pool2;
42051c0b2f7Stbbdev     pool_create_v1(0, &pol, &pool1);
42151c0b2f7Stbbdev     pool_create_v1(0, &pol, &pool2);
42251c0b2f7Stbbdev     pool_destroy(pool1);
42351c0b2f7Stbbdev     pool_destroy(pool2);
42451c0b2f7Stbbdev 
42551c0b2f7Stbbdev     scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS, NULL);
42651c0b2f7Stbbdev     beforeNumBackRef = allocatedBackRefCount();
42751c0b2f7Stbbdev     rml::MemoryPool *fixedPool;
42851c0b2f7Stbbdev 
42951c0b2f7Stbbdev     pool_create_v1(0, &pol, &fixedPool);
43051c0b2f7Stbbdev     pol.pAlloc = getMallocMem;
43151c0b2f7Stbbdev     pol.pFree = putMallocMem;
43251c0b2f7Stbbdev     pol.granularity = 8;
43351c0b2f7Stbbdev     rml::MemoryPool *mallocPool;
43451c0b2f7Stbbdev 
43551c0b2f7Stbbdev     pool_create_v1(0, &pol, &mallocPool);
43651c0b2f7Stbbdev /* check that large object cache (LOC) returns correct size for cached objects
43751c0b2f7Stbbdev    passBackendSz Byte objects are cached in LOC, but bypassed the backend, so
43851c0b2f7Stbbdev    memory requested directly from allocation callback.
43951c0b2f7Stbbdev    nextPassBackendSz Byte objects must fit to another LOC bin,
44051c0b2f7Stbbdev    so that their allocation/realeasing leads to cache cleanup.
44151c0b2f7Stbbdev    All this is expecting to lead to releasing of passBackendSz Byte object
44251c0b2f7Stbbdev    from LOC during LOC cleanup, and putMallocMem checks that returned size
44351c0b2f7Stbbdev    is correct.
44451c0b2f7Stbbdev */
44551c0b2f7Stbbdev     const size_t passBackendSz = Backend::maxBinned_HugePage+1,
44651c0b2f7Stbbdev         anotherLOCBinSz = minLargeObjectSize+1;
44751c0b2f7Stbbdev     for (int i=0; i<10; i++) { // run long enough to be cached
44851c0b2f7Stbbdev         void *p = pool_malloc(mallocPool, passBackendSz);
44951c0b2f7Stbbdev         REQUIRE_MESSAGE(p, "Memory was not allocated");
45051c0b2f7Stbbdev         pool_free(mallocPool, p);
45151c0b2f7Stbbdev     }
45251c0b2f7Stbbdev     // run long enough to passBackendSz allocation was cleaned from cache
45351c0b2f7Stbbdev     // and returned back to putMallocMem for size checking
45451c0b2f7Stbbdev     for (int i=0; i<1000; i++) {
45551c0b2f7Stbbdev         void *p = pool_malloc(mallocPool, anotherLOCBinSz);
45651c0b2f7Stbbdev         REQUIRE_MESSAGE(p, "Memory was not allocated");
45751c0b2f7Stbbdev         pool_free(mallocPool, p);
45851c0b2f7Stbbdev     }
45951c0b2f7Stbbdev 
46051c0b2f7Stbbdev     void *smallObj =  pool_malloc(fixedPool, 10);
46151c0b2f7Stbbdev     REQUIRE_MESSAGE(smallObj, "Memory was not allocated");
46251c0b2f7Stbbdev     memset(smallObj, 1, 10);
46351c0b2f7Stbbdev     void *ptr = pool_malloc(fixedPool, 1024);
46451c0b2f7Stbbdev     REQUIRE_MESSAGE(ptr, "Memory was not allocated");
46551c0b2f7Stbbdev     memset(ptr, 1, 1024);
46651c0b2f7Stbbdev     void *largeObj = pool_malloc(fixedPool, minLargeObjectSize);
46751c0b2f7Stbbdev     REQUIRE_MESSAGE(largeObj, "Memory was not allocated");
46851c0b2f7Stbbdev     memset(largeObj, 1, minLargeObjectSize);
46951c0b2f7Stbbdev     ptr = pool_malloc(fixedPool, minLargeObjectSize);
47051c0b2f7Stbbdev     REQUIRE_MESSAGE(ptr, "Memory was not allocated");
47151c0b2f7Stbbdev     memset(ptr, minLargeObjectSize, minLargeObjectSize);
47251c0b2f7Stbbdev     pool_malloc(fixedPool, 10*minLargeObjectSize); // no leak for unsuccessful allocations
47351c0b2f7Stbbdev     pool_free(fixedPool, smallObj);
47451c0b2f7Stbbdev     pool_free(fixedPool, largeObj);
47551c0b2f7Stbbdev 
47651c0b2f7Stbbdev     // provoke large object cache cleanup and hope no leaks occurs
47751c0b2f7Stbbdev     for( int p=MaxThread; p>=MinThread; --p )
47851c0b2f7Stbbdev         utils::NativeParallelFor( p, StressLOCacheWork(mallocPool) );
47951c0b2f7Stbbdev     pool_destroy(mallocPool);
48051c0b2f7Stbbdev     pool_destroy(fixedPool);
48151c0b2f7Stbbdev 
48251c0b2f7Stbbdev     scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS, NULL);
48351c0b2f7Stbbdev     afterNumBackRef = allocatedBackRefCount();
48451c0b2f7Stbbdev     REQUIRE_MESSAGE(beforeNumBackRef==afterNumBackRef, "backreference leak detected");
48551c0b2f7Stbbdev 
48651c0b2f7Stbbdev     {
48751c0b2f7Stbbdev         // test usedSize/cachedSize and LOC bitmask correctness
48851c0b2f7Stbbdev         void *p[5];
48951c0b2f7Stbbdev         pool_create_v1(0, &pol, &mallocPool);
49051c0b2f7Stbbdev         const LargeObjectCache *loc = &((rml::internal::MemoryPool*)mallocPool)->extMemPool.loc;
49151c0b2f7Stbbdev         const int LargeCacheStep = LargeObjectCache::LargeBSProps::CacheStep;
49251c0b2f7Stbbdev         p[3] = pool_malloc(mallocPool, minLargeObjectSize+2*LargeCacheStep);
49351c0b2f7Stbbdev         for (int i=0; i<10; i++) {
49451c0b2f7Stbbdev             p[0] = pool_malloc(mallocPool, minLargeObjectSize);
49551c0b2f7Stbbdev             p[1] = pool_malloc(mallocPool, minLargeObjectSize+LargeCacheStep);
49651c0b2f7Stbbdev             pool_free(mallocPool, p[0]);
49751c0b2f7Stbbdev             pool_free(mallocPool, p[1]);
49851c0b2f7Stbbdev         }
49951c0b2f7Stbbdev         REQUIRE(loc->getUsedSize());
50051c0b2f7Stbbdev         pool_free(mallocPool, p[3]);
50151c0b2f7Stbbdev         REQUIRE(loc->getLOCSize() < 3*(minLargeObjectSize+LargeCacheStep));
50251c0b2f7Stbbdev         const size_t maxLocalLOCSize = LocalLOCImpl<3,30>::getMaxSize();
50351c0b2f7Stbbdev         REQUIRE(loc->getUsedSize() <= maxLocalLOCSize);
50451c0b2f7Stbbdev         for (int i=0; i<3; i++)
50551c0b2f7Stbbdev             p[i] = pool_malloc(mallocPool, minLargeObjectSize+i*LargeCacheStep);
50651c0b2f7Stbbdev         size_t currUser = loc->getUsedSize();
50751c0b2f7Stbbdev         REQUIRE((!loc->getLOCSize() && currUser >= 3*(minLargeObjectSize+LargeCacheStep)));
50851c0b2f7Stbbdev         p[4] = pool_malloc(mallocPool, minLargeObjectSize+3*LargeCacheStep);
50951c0b2f7Stbbdev         REQUIRE(loc->getUsedSize() - currUser >= minLargeObjectSize+3*LargeCacheStep);
51051c0b2f7Stbbdev         pool_free(mallocPool, p[4]);
51151c0b2f7Stbbdev         REQUIRE(loc->getUsedSize() <= currUser+maxLocalLOCSize);
51251c0b2f7Stbbdev         pool_reset(mallocPool);
51351c0b2f7Stbbdev         REQUIRE((!loc->getLOCSize() && !loc->getUsedSize()));
51451c0b2f7Stbbdev         pool_destroy(mallocPool);
51551c0b2f7Stbbdev     }
51651c0b2f7Stbbdev     // To test LOC we need bigger lists than released by current LocalLOC
51751c0b2f7Stbbdev     //   in production code. Create special LocalLOC.
51851c0b2f7Stbbdev     {
51951c0b2f7Stbbdev         LocalLOCImpl<2, 20> lLOC;
52051c0b2f7Stbbdev         pool_create_v1(0, &pol, &mallocPool);
52151c0b2f7Stbbdev         rml::internal::ExtMemoryPool *mPool = &((rml::internal::MemoryPool*)mallocPool)->extMemPool;
52251c0b2f7Stbbdev         const LargeObjectCache *loc = &((rml::internal::MemoryPool*)mallocPool)->extMemPool.loc;
52351c0b2f7Stbbdev         const int LargeCacheStep = LargeObjectCache::LargeBSProps::CacheStep;
52451c0b2f7Stbbdev         for (int i=0; i<22; i++) {
52551c0b2f7Stbbdev             void *o = pool_malloc(mallocPool, minLargeObjectSize+i*LargeCacheStep);
52651c0b2f7Stbbdev             bool ret = lLOC.put(((LargeObjectHdr*)o - 1)->memoryBlock, mPool);
52751c0b2f7Stbbdev             REQUIRE(ret);
52851c0b2f7Stbbdev 
52951c0b2f7Stbbdev             o = pool_malloc(mallocPool, minLargeObjectSize+i*LargeCacheStep);
53051c0b2f7Stbbdev             ret = lLOC.put(((LargeObjectHdr*)o - 1)->memoryBlock, mPool);
53151c0b2f7Stbbdev             REQUIRE(ret);
53251c0b2f7Stbbdev         }
53351c0b2f7Stbbdev         lLOC.externalCleanup(mPool);
53451c0b2f7Stbbdev         REQUIRE(!loc->getUsedSize());
53551c0b2f7Stbbdev 
53651c0b2f7Stbbdev         pool_destroy(mallocPool);
53751c0b2f7Stbbdev     }
53851c0b2f7Stbbdev }
53951c0b2f7Stbbdev 
54051c0b2f7Stbbdev void TestObjectRecognition() {
54151c0b2f7Stbbdev     size_t headersSize = sizeof(LargeMemoryBlock)+sizeof(LargeObjectHdr);
54251c0b2f7Stbbdev     unsigned falseObjectSize = 113; // unsigned is the type expected by getObjectSize
54351c0b2f7Stbbdev     size_t obtainedSize;
54451c0b2f7Stbbdev 
54551c0b2f7Stbbdev     REQUIRE_MESSAGE(sizeof(BackRefIdx)==sizeof(uintptr_t), "Unexpected size of BackRefIdx");
54651c0b2f7Stbbdev     REQUIRE_MESSAGE(getObjectSize(falseObjectSize)!=falseObjectSize, "Error in test: bad choice for false object size");
54751c0b2f7Stbbdev 
54851c0b2f7Stbbdev     void* mem = scalable_malloc(2*slabSize);
54951c0b2f7Stbbdev     REQUIRE_MESSAGE(mem, "Memory was not allocated");
55051c0b2f7Stbbdev     Block* falseBlock = (Block*)alignUp((uintptr_t)mem, slabSize);
55151c0b2f7Stbbdev     falseBlock->objectSize = falseObjectSize;
55251c0b2f7Stbbdev     char* falseSO = (char*)falseBlock + falseObjectSize*7;
55351c0b2f7Stbbdev     REQUIRE_MESSAGE(alignDown(falseSO, slabSize)==(void*)falseBlock, "Error in test: false object offset is too big");
55451c0b2f7Stbbdev 
55551c0b2f7Stbbdev     void* bufferLOH = scalable_malloc(2*slabSize + headersSize);
55651c0b2f7Stbbdev     REQUIRE_MESSAGE(bufferLOH, "Memory was not allocated");
55751c0b2f7Stbbdev     LargeObjectHdr* falseLO =
55851c0b2f7Stbbdev         (LargeObjectHdr*)alignUp((uintptr_t)bufferLOH + headersSize, slabSize);
55951c0b2f7Stbbdev     LargeObjectHdr* headerLO = (LargeObjectHdr*)falseLO-1;
56051c0b2f7Stbbdev     headerLO->memoryBlock = (LargeMemoryBlock*)bufferLOH;
56151c0b2f7Stbbdev     headerLO->memoryBlock->unalignedSize = 2*slabSize + headersSize;
56251c0b2f7Stbbdev     headerLO->memoryBlock->objectSize = slabSize + headersSize;
56351c0b2f7Stbbdev     headerLO->backRefIdx = BackRefIdx::newBackRef(/*largeObj=*/true);
56451c0b2f7Stbbdev     setBackRef(headerLO->backRefIdx, headerLO);
56551c0b2f7Stbbdev     REQUIRE_MESSAGE(scalable_msize(falseLO) == slabSize + headersSize,
56651c0b2f7Stbbdev            "Error in test: LOH falsification failed");
56751c0b2f7Stbbdev     removeBackRef(headerLO->backRefIdx);
56851c0b2f7Stbbdev 
56951c0b2f7Stbbdev     const int NUM_OF_IDX = BR_MAX_CNT+2;
57051c0b2f7Stbbdev     BackRefIdx idxs[NUM_OF_IDX];
57151c0b2f7Stbbdev     for (int cnt=0; cnt<2; cnt++) {
57251c0b2f7Stbbdev         for (int master = -10; master<10; master++) {
57351c0b2f7Stbbdev             falseBlock->backRefIdx.master = (uint16_t)master;
57451c0b2f7Stbbdev             headerLO->backRefIdx.master = (uint16_t)master;
57551c0b2f7Stbbdev 
57651c0b2f7Stbbdev             for (int bl = -10; bl<BR_MAX_CNT+10; bl++) {
57751c0b2f7Stbbdev                 falseBlock->backRefIdx.offset = (uint16_t)bl;
57851c0b2f7Stbbdev                 headerLO->backRefIdx.offset = (uint16_t)bl;
57951c0b2f7Stbbdev 
58051c0b2f7Stbbdev                 for (int largeObj = 0; largeObj<2; largeObj++) {
58151c0b2f7Stbbdev                     falseBlock->backRefIdx.largeObj = largeObj;
58251c0b2f7Stbbdev                     headerLO->backRefIdx.largeObj = largeObj;
58351c0b2f7Stbbdev 
58451c0b2f7Stbbdev                     obtainedSize = __TBB_malloc_safer_msize(falseSO, NULL);
58551c0b2f7Stbbdev                     REQUIRE_MESSAGE(obtainedSize==0, "Incorrect pointer accepted");
58651c0b2f7Stbbdev                     obtainedSize = __TBB_malloc_safer_msize(falseLO, NULL);
58751c0b2f7Stbbdev                     REQUIRE_MESSAGE(obtainedSize==0, "Incorrect pointer accepted");
58851c0b2f7Stbbdev                 }
58951c0b2f7Stbbdev             }
59051c0b2f7Stbbdev         }
59151c0b2f7Stbbdev         if (cnt == 1) {
59251c0b2f7Stbbdev             for (int i=0; i<NUM_OF_IDX; i++)
59351c0b2f7Stbbdev                 removeBackRef(idxs[i]);
59451c0b2f7Stbbdev             break;
59551c0b2f7Stbbdev         }
59651c0b2f7Stbbdev         for (int i=0; i<NUM_OF_IDX; i++) {
59751c0b2f7Stbbdev             idxs[i] = BackRefIdx::newBackRef(/*largeObj=*/false);
59851c0b2f7Stbbdev             setBackRef(idxs[i], NULL);
59951c0b2f7Stbbdev         }
60051c0b2f7Stbbdev     }
60151c0b2f7Stbbdev     char *smallPtr = (char*)scalable_malloc(falseObjectSize);
60251c0b2f7Stbbdev     obtainedSize = __TBB_malloc_safer_msize(smallPtr, NULL);
60351c0b2f7Stbbdev     REQUIRE_MESSAGE(obtainedSize==getObjectSize(falseObjectSize), "Correct pointer not accepted?");
60451c0b2f7Stbbdev     scalable_free(smallPtr);
60551c0b2f7Stbbdev 
60651c0b2f7Stbbdev     obtainedSize = __TBB_malloc_safer_msize(mem, NULL);
60751c0b2f7Stbbdev     REQUIRE_MESSAGE(obtainedSize>=2*slabSize, "Correct pointer not accepted?");
60851c0b2f7Stbbdev     scalable_free(mem);
60951c0b2f7Stbbdev     scalable_free(bufferLOH);
61051c0b2f7Stbbdev }
61151c0b2f7Stbbdev 
61251c0b2f7Stbbdev class TestBackendWork: public SimpleBarrier {
61351c0b2f7Stbbdev     struct TestBlock {
61451c0b2f7Stbbdev         intptr_t   data;
61551c0b2f7Stbbdev         BackRefIdx idx;
61651c0b2f7Stbbdev     };
61751c0b2f7Stbbdev     static const int ITERS = 20;
61851c0b2f7Stbbdev 
61951c0b2f7Stbbdev     rml::internal::Backend *backend;
62051c0b2f7Stbbdev public:
62151c0b2f7Stbbdev     TestBackendWork(rml::internal::Backend *bknd) : backend(bknd) {}
62251c0b2f7Stbbdev     void operator()(int) const {
62351c0b2f7Stbbdev         barrier.wait();
62451c0b2f7Stbbdev 
62551c0b2f7Stbbdev         for (int i=0; i<ITERS; i++) {
62651c0b2f7Stbbdev             BlockI *slabBlock = backend->getSlabBlock(1);
62751c0b2f7Stbbdev             REQUIRE_MESSAGE(slabBlock, "Memory was not allocated");
62851c0b2f7Stbbdev             uintptr_t prevBlock = (uintptr_t)slabBlock;
62951c0b2f7Stbbdev             backend->putSlabBlock(slabBlock);
63051c0b2f7Stbbdev 
63151c0b2f7Stbbdev             LargeMemoryBlock *largeBlock = backend->getLargeBlock(16*1024);
63251c0b2f7Stbbdev             REQUIRE_MESSAGE(largeBlock, "Memory was not allocated");
63351c0b2f7Stbbdev             REQUIRE_MESSAGE((uintptr_t)largeBlock != prevBlock,
63451c0b2f7Stbbdev                     "Large block cannot be reused from slab memory, only in fixed_pool case.");
63551c0b2f7Stbbdev             backend->putLargeBlock(largeBlock);
63651c0b2f7Stbbdev         }
63751c0b2f7Stbbdev     }
63851c0b2f7Stbbdev };
63951c0b2f7Stbbdev 
64051c0b2f7Stbbdev void TestBackend()
64151c0b2f7Stbbdev {
64251c0b2f7Stbbdev     rml::MemPoolPolicy pol(getMallocMem, putMallocMem);
64351c0b2f7Stbbdev     rml::MemoryPool *mPool;
64451c0b2f7Stbbdev     pool_create_v1(0, &pol, &mPool);
64551c0b2f7Stbbdev     rml::internal::ExtMemoryPool *ePool = &((rml::internal::MemoryPool*)mPool)->extMemPool;
64651c0b2f7Stbbdev     rml::internal::Backend *backend = &ePool->backend;
64751c0b2f7Stbbdev 
64851c0b2f7Stbbdev     for( int p=MaxThread; p>=MinThread; --p ) {
64951c0b2f7Stbbdev         // regression test against an race condition in backend synchronization,
65051c0b2f7Stbbdev         // triggered only when WhiteboxTestingYield() call yields
65151c0b2f7Stbbdev         for (int i=0; i<100; i++) {
65251c0b2f7Stbbdev             TestBackendWork::initBarrier(p);
65351c0b2f7Stbbdev             utils::NativeParallelFor( p, TestBackendWork(backend) );
65451c0b2f7Stbbdev         }
65551c0b2f7Stbbdev     }
65651c0b2f7Stbbdev 
65751c0b2f7Stbbdev     BlockI *block = backend->getSlabBlock(1);
65851c0b2f7Stbbdev     REQUIRE_MESSAGE(block, "Memory was not allocated");
65951c0b2f7Stbbdev     backend->putSlabBlock(block);
66051c0b2f7Stbbdev 
66151c0b2f7Stbbdev     // Checks if the backend increases and decreases the amount of allocated memory when memory is allocated.
66251c0b2f7Stbbdev     const size_t memSize0 = backend->getTotalMemSize();
66351c0b2f7Stbbdev     LargeMemoryBlock *lmb = backend->getLargeBlock(4*MByte);
66451c0b2f7Stbbdev     REQUIRE( lmb );
66551c0b2f7Stbbdev 
66651c0b2f7Stbbdev     const size_t memSize1 = backend->getTotalMemSize();
66751c0b2f7Stbbdev     REQUIRE_MESSAGE( (intptr_t)(memSize1-memSize0) >= 4*MByte, "The backend has not increased the amount of using memory." );
66851c0b2f7Stbbdev 
66951c0b2f7Stbbdev     backend->putLargeBlock(lmb);
67051c0b2f7Stbbdev     const size_t memSize2 = backend->getTotalMemSize();
67151c0b2f7Stbbdev     REQUIRE_MESSAGE( memSize2 == memSize0, "The backend has not decreased the amount of using memory." );
67251c0b2f7Stbbdev 
67351c0b2f7Stbbdev     pool_destroy(mPool);
67451c0b2f7Stbbdev }
67551c0b2f7Stbbdev 
67651c0b2f7Stbbdev void TestBitMask()
67751c0b2f7Stbbdev {
67851c0b2f7Stbbdev     BitMaskMin<256> mask;
67951c0b2f7Stbbdev 
68051c0b2f7Stbbdev     mask.reset();
68151c0b2f7Stbbdev     mask.set(10, 1);
68251c0b2f7Stbbdev     mask.set(5, 1);
68351c0b2f7Stbbdev     mask.set(1, 1);
68451c0b2f7Stbbdev     REQUIRE(mask.getMinTrue(2) == 5);
68551c0b2f7Stbbdev 
68651c0b2f7Stbbdev     mask.reset();
68751c0b2f7Stbbdev     mask.set(0, 1);
68851c0b2f7Stbbdev     mask.set(64, 1);
68951c0b2f7Stbbdev     mask.set(63, 1);
69051c0b2f7Stbbdev     mask.set(200, 1);
69151c0b2f7Stbbdev     mask.set(255, 1);
69251c0b2f7Stbbdev     REQUIRE(mask.getMinTrue(0) == 0);
69351c0b2f7Stbbdev     REQUIRE(mask.getMinTrue(1) == 63);
69451c0b2f7Stbbdev     REQUIRE(mask.getMinTrue(63) == 63);
69551c0b2f7Stbbdev     REQUIRE(mask.getMinTrue(64) == 64);
69651c0b2f7Stbbdev     REQUIRE(mask.getMinTrue(101) == 200);
69751c0b2f7Stbbdev     REQUIRE(mask.getMinTrue(201) == 255);
69851c0b2f7Stbbdev     mask.set(255, 0);
69951c0b2f7Stbbdev     REQUIRE(mask.getMinTrue(201) == -1);
70051c0b2f7Stbbdev }
70151c0b2f7Stbbdev 
70251c0b2f7Stbbdev size_t getMemSize()
70351c0b2f7Stbbdev {
70451c0b2f7Stbbdev     return defaultMemPool->extMemPool.backend.getTotalMemSize();
70551c0b2f7Stbbdev }
70651c0b2f7Stbbdev 
70751c0b2f7Stbbdev class CheckNotCached {
70851c0b2f7Stbbdev     static size_t memSize;
70951c0b2f7Stbbdev public:
71051c0b2f7Stbbdev     void operator() () const {
71151c0b2f7Stbbdev         int res = scalable_allocation_mode(TBBMALLOC_SET_SOFT_HEAP_LIMIT, 1);
71251c0b2f7Stbbdev         REQUIRE(res == TBBMALLOC_OK);
71351c0b2f7Stbbdev         if (memSize==(size_t)-1) {
71451c0b2f7Stbbdev             memSize = getMemSize();
71551c0b2f7Stbbdev         } else {
71651c0b2f7Stbbdev             REQUIRE(getMemSize() == memSize);
71751c0b2f7Stbbdev             memSize=(size_t)-1;
71851c0b2f7Stbbdev         }
71951c0b2f7Stbbdev     }
72051c0b2f7Stbbdev };
72151c0b2f7Stbbdev 
72251c0b2f7Stbbdev size_t CheckNotCached::memSize = (size_t)-1;
72351c0b2f7Stbbdev 
72451c0b2f7Stbbdev class RunTestHeapLimit: public SimpleBarrier {
72551c0b2f7Stbbdev public:
72651c0b2f7Stbbdev     void operator()( int /*mynum*/ ) const {
72751c0b2f7Stbbdev         // Provoke bootstrap heap initialization before recording memory size.
72851c0b2f7Stbbdev         // NOTE: The initialization should be processed only with a "large"
72951c0b2f7Stbbdev         // object. Since the "small" object allocation lead to blocking of a
73051c0b2f7Stbbdev         // slab as an active block and it is impossible to release it with
73151c0b2f7Stbbdev         // foreign thread.
73251c0b2f7Stbbdev         scalable_free(scalable_malloc(minLargeObjectSize));
73351c0b2f7Stbbdev         barrier.wait(CheckNotCached());
73451c0b2f7Stbbdev         for (size_t n = minLargeObjectSize; n < 5*1024*1024; n += 128*1024)
73551c0b2f7Stbbdev             scalable_free(scalable_malloc(n));
73651c0b2f7Stbbdev         barrier.wait(CheckNotCached());
73751c0b2f7Stbbdev     }
73851c0b2f7Stbbdev };
73951c0b2f7Stbbdev 
74051c0b2f7Stbbdev void TestHeapLimit()
74151c0b2f7Stbbdev {
74251c0b2f7Stbbdev     if(!isMallocInitialized()) doInitialization();
74351c0b2f7Stbbdev     // tiny limit to stop caching
74451c0b2f7Stbbdev     int res = scalable_allocation_mode(TBBMALLOC_SET_SOFT_HEAP_LIMIT, 1);
74551c0b2f7Stbbdev     REQUIRE(res == TBBMALLOC_OK);
74651c0b2f7Stbbdev      // Provoke bootstrap heap initialization before recording memory size.
74751c0b2f7Stbbdev     scalable_free(scalable_malloc(8));
74851c0b2f7Stbbdev     size_t n, sizeBefore = getMemSize();
74951c0b2f7Stbbdev 
75051c0b2f7Stbbdev     // Try to provoke call to OS for memory to check that
75151c0b2f7Stbbdev     // requests are not fulfilled from caches.
75251c0b2f7Stbbdev     // Single call is not enough here because of backend fragmentation.
75351c0b2f7Stbbdev     for (n = minLargeObjectSize; n < 10*1024*1024; n += 16*1024) {
75451c0b2f7Stbbdev         void *p = scalable_malloc(n);
75551c0b2f7Stbbdev         bool leave = (sizeBefore != getMemSize());
75651c0b2f7Stbbdev         scalable_free(p);
75751c0b2f7Stbbdev         if (leave)
75851c0b2f7Stbbdev             break;
75951c0b2f7Stbbdev         REQUIRE_MESSAGE(sizeBefore == getMemSize(), "No caching expected");
76051c0b2f7Stbbdev     }
76151c0b2f7Stbbdev     REQUIRE_MESSAGE(n < 10*1024*1024, "scalable_malloc doesn't provoke OS request for memory, "
76251c0b2f7Stbbdev            "is some internal cache still used?");
76351c0b2f7Stbbdev 
76451c0b2f7Stbbdev     for( int p=MaxThread; p>=MinThread; --p ) {
76551c0b2f7Stbbdev         RunTestHeapLimit::initBarrier( p );
76651c0b2f7Stbbdev         utils::NativeParallelFor( p, RunTestHeapLimit() );
76751c0b2f7Stbbdev     }
76851c0b2f7Stbbdev     // it's try to match limit as well as set limit, so call here
76951c0b2f7Stbbdev     res = scalable_allocation_mode(TBBMALLOC_SET_SOFT_HEAP_LIMIT, 1);
77051c0b2f7Stbbdev     REQUIRE(res == TBBMALLOC_OK);
77151c0b2f7Stbbdev     size_t m = getMemSize();
77251c0b2f7Stbbdev     REQUIRE(sizeBefore == m);
77351c0b2f7Stbbdev     // restore default
77451c0b2f7Stbbdev     res = scalable_allocation_mode(TBBMALLOC_SET_SOFT_HEAP_LIMIT, 0);
77551c0b2f7Stbbdev     REQUIRE(res == TBBMALLOC_OK);
77651c0b2f7Stbbdev }
77751c0b2f7Stbbdev 
77851c0b2f7Stbbdev void checkNoHugePages()
77951c0b2f7Stbbdev {
78051c0b2f7Stbbdev     REQUIRE_MESSAGE(!hugePages.isEnabled, "scalable_allocation_mode "
78151c0b2f7Stbbdev            "must have priority over environment variable");
78251c0b2f7Stbbdev }
78351c0b2f7Stbbdev 
78451c0b2f7Stbbdev /*---------------------------------------------------------------------------*/
78551c0b2f7Stbbdev // The regression test against bugs in TBBMALLOC_CLEAN_ALL_BUFFERS allocation command.
78651c0b2f7Stbbdev // The idea is to allocate and deallocate a set of objects randomly in parallel.
78751c0b2f7Stbbdev // For large sizes (16K), it forces conflicts in backend during coalescing.
78851c0b2f7Stbbdev // For small sizes (4K), it forces cross-thread deallocations and then orphaned slabs.
78951c0b2f7Stbbdev // Global cleanup should process orphaned slabs and the queue of postponed coalescing
79051c0b2f7Stbbdev // requests, otherwise it will not be able to unmap all unused memory.
79151c0b2f7Stbbdev 
79251c0b2f7Stbbdev const int num_allocs = 10*1024;
79351c0b2f7Stbbdev void *ptrs[num_allocs];
79451c0b2f7Stbbdev std::atomic<int> alloc_counter;
79551c0b2f7Stbbdev 
79651c0b2f7Stbbdev inline void multiThreadAlloc(size_t alloc_size) {
79751c0b2f7Stbbdev     for( int i = alloc_counter++; i < num_allocs; i = alloc_counter++ ) {
79851c0b2f7Stbbdev        ptrs[i] = scalable_malloc( alloc_size );
79951c0b2f7Stbbdev        REQUIRE_MESSAGE( ptrs[i] != nullptr, "scalable_malloc returned zero." );
80051c0b2f7Stbbdev     }
80151c0b2f7Stbbdev }
80251c0b2f7Stbbdev inline void crossThreadDealloc() {
80351c0b2f7Stbbdev     for( int i = --alloc_counter; i >= 0; i = --alloc_counter ) {
80451c0b2f7Stbbdev        if (i < num_allocs) scalable_free( ptrs[i] );
80551c0b2f7Stbbdev     }
80651c0b2f7Stbbdev }
80751c0b2f7Stbbdev 
80851c0b2f7Stbbdev template<int AllocSize>
80951c0b2f7Stbbdev struct TestCleanAllBuffersBody : public SimpleBarrier {
81051c0b2f7Stbbdev     void operator() ( int ) const {
81151c0b2f7Stbbdev         barrier.wait();
81251c0b2f7Stbbdev         multiThreadAlloc(AllocSize);
81351c0b2f7Stbbdev         barrier.wait();
81451c0b2f7Stbbdev         crossThreadDealloc();
81551c0b2f7Stbbdev     }
81651c0b2f7Stbbdev };
81751c0b2f7Stbbdev 
81851c0b2f7Stbbdev template<int AllocSize>
81951c0b2f7Stbbdev void TestCleanAllBuffers() {
82051c0b2f7Stbbdev     const int num_threads = 8;
82151c0b2f7Stbbdev     // Clean up if something was allocated before the test
82251c0b2f7Stbbdev     scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS,0);
82351c0b2f7Stbbdev 
82451c0b2f7Stbbdev     size_t memory_in_use_before = getMemSize();
82551c0b2f7Stbbdev     alloc_counter = 0;
82651c0b2f7Stbbdev     TestCleanAllBuffersBody<AllocSize>::initBarrier(num_threads);
82751c0b2f7Stbbdev 
82851c0b2f7Stbbdev     utils::NativeParallelFor(num_threads, TestCleanAllBuffersBody<AllocSize>());
82951c0b2f7Stbbdev     // TODO: reproduce the bug conditions more reliably
83051c0b2f7Stbbdev     if ( defaultMemPool->extMemPool.backend.coalescQ.blocksToFree.load(std::memory_order_relaxed) == NULL ) {
83151c0b2f7Stbbdev         INFO( "Warning: The queue of postponed coalescing requests is empty. ");
83251c0b2f7Stbbdev         INFO( "Unable to create the condition for bug reproduction.\n" );
83351c0b2f7Stbbdev     }
83451c0b2f7Stbbdev     int result = scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS,0);
83551c0b2f7Stbbdev     REQUIRE_MESSAGE( result == TBBMALLOC_OK, "The cleanup request has not cleaned anything." );
83651c0b2f7Stbbdev     size_t memory_in_use_after = getMemSize();
83751c0b2f7Stbbdev 
83851c0b2f7Stbbdev     size_t memory_leak = memory_in_use_after - memory_in_use_before;
83951c0b2f7Stbbdev     INFO( "memory_in_use_before = " <<  memory_in_use_before << ", memory_in_use_after = " << memory_in_use_after << "\n" );
84051c0b2f7Stbbdev     REQUIRE_MESSAGE( memory_leak == 0, "Cleanup was unable to release all allocated memory." );
84151c0b2f7Stbbdev }
84251c0b2f7Stbbdev 
84351c0b2f7Stbbdev //! Force cross thread deallocation of small objects to create a set of privatizable slab blocks.
84451c0b2f7Stbbdev //! TBBMALLOC_CLEAN_THREAD_BUFFERS command have to privatize all the block.
84551c0b2f7Stbbdev struct TestCleanThreadBuffersBody : public SimpleBarrier {
84651c0b2f7Stbbdev     void operator() ( int ) const {
84751c0b2f7Stbbdev         barrier.wait();
84851c0b2f7Stbbdev         multiThreadAlloc(2*1024);
84951c0b2f7Stbbdev         barrier.wait();
85051c0b2f7Stbbdev         crossThreadDealloc();
85151c0b2f7Stbbdev         barrier.wait();
85251c0b2f7Stbbdev         int result = scalable_allocation_command(TBBMALLOC_CLEAN_THREAD_BUFFERS,0);
85351c0b2f7Stbbdev         if (result != TBBMALLOC_OK) {
85451c0b2f7Stbbdev             REPORT("Warning: clean-up request for this particular thread has not cleaned anything.");
85551c0b2f7Stbbdev         }
85651c0b2f7Stbbdev 
85751c0b2f7Stbbdev         // Check that TLS was cleaned fully
85851c0b2f7Stbbdev         TLSData *tlsCurr = defaultMemPool->getTLS(/*create=*/false);
85951c0b2f7Stbbdev         for (int i = 0; i < numBlockBinLimit; i++) {
86051c0b2f7Stbbdev             REQUIRE_MESSAGE(!(tlsCurr->bin[i].activeBlk), "Some bin was not cleaned.");
86151c0b2f7Stbbdev         }
86251c0b2f7Stbbdev         REQUIRE_MESSAGE(!(tlsCurr->lloc.head.load(std::memory_order_relaxed)), "Local LOC was not cleaned.");
86351c0b2f7Stbbdev         REQUIRE_MESSAGE(!(tlsCurr->freeSlabBlocks.head.load(std::memory_order_relaxed)), "Free Block pool was not cleaned.");
86451c0b2f7Stbbdev     }
86551c0b2f7Stbbdev };
86651c0b2f7Stbbdev 
86751c0b2f7Stbbdev void TestCleanThreadBuffers() {
86851c0b2f7Stbbdev     const int num_threads = 8;
86951c0b2f7Stbbdev     // Clean up if something was allocated before the test
87051c0b2f7Stbbdev     scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS,0);
87151c0b2f7Stbbdev 
87251c0b2f7Stbbdev     alloc_counter = 0;
87351c0b2f7Stbbdev     TestCleanThreadBuffersBody::initBarrier(num_threads);
87451c0b2f7Stbbdev     utils::NativeParallelFor(num_threads, TestCleanThreadBuffersBody());
87551c0b2f7Stbbdev }
87651c0b2f7Stbbdev 
87751c0b2f7Stbbdev /*---------------------------------------------------------------------------*/
87851c0b2f7Stbbdev /*------------------------- Large Object Cache tests ------------------------*/
87951c0b2f7Stbbdev #if _MSC_VER==1600 || _MSC_VER==1500
88051c0b2f7Stbbdev     // ignore C4275: non dll-interface class 'stdext::exception' used as
88151c0b2f7Stbbdev     // base for dll-interface class 'std::bad_cast'
88251c0b2f7Stbbdev     #pragma warning (disable: 4275)
88351c0b2f7Stbbdev #endif
88451c0b2f7Stbbdev #include <vector>
88551c0b2f7Stbbdev #include <list>
88651c0b2f7Stbbdev 
88751c0b2f7Stbbdev // default constructor of CacheBin
88851c0b2f7Stbbdev template<typename Props>
88951c0b2f7Stbbdev rml::internal::LargeObjectCacheImpl<Props>::CacheBin::CacheBin() {}
89051c0b2f7Stbbdev 
89151c0b2f7Stbbdev template<typename Props>
89251c0b2f7Stbbdev class CacheBinModel {
89351c0b2f7Stbbdev 
89451c0b2f7Stbbdev     typedef typename rml::internal::LargeObjectCacheImpl<Props>::CacheBin CacheBinType;
89551c0b2f7Stbbdev 
89651c0b2f7Stbbdev     // The emulated cache bin.
89751c0b2f7Stbbdev     CacheBinType cacheBinModel;
89851c0b2f7Stbbdev     // The reference to real cache bin inside the large object cache.
89951c0b2f7Stbbdev     CacheBinType &cacheBin;
90051c0b2f7Stbbdev 
90151c0b2f7Stbbdev     const size_t size;
90251c0b2f7Stbbdev 
90351c0b2f7Stbbdev     // save only current time
90451c0b2f7Stbbdev     std::list<uintptr_t> objects;
90551c0b2f7Stbbdev 
90651c0b2f7Stbbdev     void doCleanup() {
90751c0b2f7Stbbdev         if ( cacheBinModel.cachedSize > Props::TooLargeFactor*cacheBinModel.usedSize ) tooLargeLOC++;
90851c0b2f7Stbbdev         else tooLargeLOC = 0;
90951c0b2f7Stbbdev 
91051c0b2f7Stbbdev         if (tooLargeLOC>3 && cacheBinModel.ageThreshold)
91151c0b2f7Stbbdev             cacheBinModel.ageThreshold = (cacheBinModel.ageThreshold + cacheBinModel.meanHitRange)/2;
91251c0b2f7Stbbdev 
91351c0b2f7Stbbdev         uintptr_t currTime = cacheCurrTime;
91451c0b2f7Stbbdev         while (!objects.empty() && (intptr_t)(currTime - objects.front()) > cacheBinModel.ageThreshold) {
91551c0b2f7Stbbdev             cacheBinModel.cachedSize -= size;
91651c0b2f7Stbbdev             cacheBinModel.lastCleanedAge = objects.front();
91751c0b2f7Stbbdev             objects.pop_front();
91851c0b2f7Stbbdev         }
91951c0b2f7Stbbdev 
92051c0b2f7Stbbdev         cacheBinModel.oldest = objects.empty() ? 0 : objects.front();
92151c0b2f7Stbbdev     }
92251c0b2f7Stbbdev 
92351c0b2f7Stbbdev public:
92451c0b2f7Stbbdev     CacheBinModel(CacheBinType &_cacheBin, size_t allocSize) : cacheBin(_cacheBin), size(allocSize) {
92551c0b2f7Stbbdev         cacheBinModel.oldest = cacheBin.oldest;
92651c0b2f7Stbbdev         cacheBinModel.lastCleanedAge = cacheBin.lastCleanedAge;
92751c0b2f7Stbbdev         cacheBinModel.ageThreshold = cacheBin.ageThreshold;
92851c0b2f7Stbbdev         cacheBinModel.usedSize = cacheBin.usedSize;
92951c0b2f7Stbbdev         cacheBinModel.cachedSize = cacheBin.cachedSize;
93051c0b2f7Stbbdev         cacheBinModel.meanHitRange = cacheBin.meanHitRange;
93151c0b2f7Stbbdev         cacheBinModel.lastGet = cacheBin.lastGet;
93251c0b2f7Stbbdev     }
93351c0b2f7Stbbdev     void get() {
93451c0b2f7Stbbdev         uintptr_t currTime = ++cacheCurrTime;
93551c0b2f7Stbbdev 
93651c0b2f7Stbbdev         if ( objects.empty() ) {
93751c0b2f7Stbbdev             const uintptr_t sinceLastGet = currTime - cacheBinModel.lastGet;
93851c0b2f7Stbbdev             if ( ( cacheBinModel.ageThreshold && sinceLastGet > Props::LongWaitFactor*cacheBinModel.ageThreshold ) ||
93951c0b2f7Stbbdev                  ( cacheBinModel.lastCleanedAge && sinceLastGet > Props::LongWaitFactor*(cacheBinModel.lastCleanedAge - cacheBinModel.lastGet) ) )
94051c0b2f7Stbbdev                 cacheBinModel.lastCleanedAge = cacheBinModel.ageThreshold = 0;
94151c0b2f7Stbbdev 
94251c0b2f7Stbbdev             if (cacheBinModel.lastCleanedAge)
94351c0b2f7Stbbdev                 cacheBinModel.ageThreshold = Props::OnMissFactor*(currTime - cacheBinModel.lastCleanedAge);
94451c0b2f7Stbbdev         } else {
94551c0b2f7Stbbdev             uintptr_t obj_age = objects.back();
94651c0b2f7Stbbdev             objects.pop_back();
94751c0b2f7Stbbdev             if ( objects.empty() ) cacheBinModel.oldest = 0;
94851c0b2f7Stbbdev 
94951c0b2f7Stbbdev             intptr_t hitRange = currTime - obj_age;
95051c0b2f7Stbbdev             cacheBinModel.meanHitRange = cacheBinModel.meanHitRange? (cacheBinModel.meanHitRange + hitRange)/2 : hitRange;
95151c0b2f7Stbbdev 
95251c0b2f7Stbbdev             cacheBinModel.cachedSize -= size;
95351c0b2f7Stbbdev         }
95451c0b2f7Stbbdev 
95551c0b2f7Stbbdev         cacheBinModel.usedSize += size;
95651c0b2f7Stbbdev         cacheBinModel.lastGet = currTime;
95751c0b2f7Stbbdev 
95851c0b2f7Stbbdev         if ( currTime % rml::internal::cacheCleanupFreq == 0 ) doCleanup();
95951c0b2f7Stbbdev     }
96051c0b2f7Stbbdev 
96151c0b2f7Stbbdev     void putList( int num ) {
96251c0b2f7Stbbdev         uintptr_t currTime = cacheCurrTime;
96351c0b2f7Stbbdev         cacheCurrTime += num;
96451c0b2f7Stbbdev 
96551c0b2f7Stbbdev         cacheBinModel.usedSize -= num*size;
96651c0b2f7Stbbdev 
96751c0b2f7Stbbdev         bool cleanUpNeeded = false;
96851c0b2f7Stbbdev         if ( !cacheBinModel.lastCleanedAge ) {
96951c0b2f7Stbbdev             cacheBinModel.lastCleanedAge = ++currTime;
97051c0b2f7Stbbdev             cleanUpNeeded |= currTime % rml::internal::cacheCleanupFreq == 0;
97151c0b2f7Stbbdev             num--;
97251c0b2f7Stbbdev         }
97351c0b2f7Stbbdev 
97451c0b2f7Stbbdev         for ( int i=1; i<=num; ++i ) {
97551c0b2f7Stbbdev             currTime+=1;
97651c0b2f7Stbbdev             cleanUpNeeded |= currTime % rml::internal::cacheCleanupFreq == 0;
97751c0b2f7Stbbdev             if ( objects.empty() )
97851c0b2f7Stbbdev                 cacheBinModel.oldest = currTime;
97951c0b2f7Stbbdev             objects.push_back(currTime);
98051c0b2f7Stbbdev         }
98151c0b2f7Stbbdev 
98251c0b2f7Stbbdev         cacheBinModel.cachedSize += num*size;
98351c0b2f7Stbbdev 
98451c0b2f7Stbbdev         if ( cleanUpNeeded ) doCleanup();
98551c0b2f7Stbbdev     }
98651c0b2f7Stbbdev 
98751c0b2f7Stbbdev     void check() {
98851c0b2f7Stbbdev         REQUIRE(cacheBinModel.oldest == cacheBin.oldest);
98951c0b2f7Stbbdev         REQUIRE(cacheBinModel.lastCleanedAge == cacheBin.lastCleanedAge);
99051c0b2f7Stbbdev         REQUIRE(cacheBinModel.ageThreshold == cacheBin.ageThreshold);
99151c0b2f7Stbbdev         REQUIRE(cacheBinModel.usedSize == cacheBin.usedSize);
99251c0b2f7Stbbdev         REQUIRE(cacheBinModel.cachedSize == cacheBin.cachedSize);
99351c0b2f7Stbbdev         REQUIRE(cacheBinModel.meanHitRange == cacheBin.meanHitRange);
99451c0b2f7Stbbdev         REQUIRE(cacheBinModel.lastGet == cacheBin.lastGet);
99551c0b2f7Stbbdev     }
99651c0b2f7Stbbdev 
99751c0b2f7Stbbdev     static uintptr_t cacheCurrTime;
99851c0b2f7Stbbdev     static intptr_t tooLargeLOC;
99951c0b2f7Stbbdev };
100051c0b2f7Stbbdev 
100151c0b2f7Stbbdev template<typename Props> uintptr_t CacheBinModel<Props>::cacheCurrTime;
100251c0b2f7Stbbdev template<typename Props> intptr_t CacheBinModel<Props>::tooLargeLOC;
100351c0b2f7Stbbdev 
100451c0b2f7Stbbdev template <typename Scenario>
100551c0b2f7Stbbdev void LOCModelTester() {
100651c0b2f7Stbbdev     defaultMemPool->extMemPool.loc.cleanAll();
100751c0b2f7Stbbdev     defaultMemPool->extMemPool.loc.reset();
100851c0b2f7Stbbdev 
100951c0b2f7Stbbdev     const size_t size = 16 * 1024;
101051c0b2f7Stbbdev     const size_t headersSize = sizeof(rml::internal::LargeMemoryBlock)+sizeof(rml::internal::LargeObjectHdr);
101151c0b2f7Stbbdev     const size_t allocationSize = LargeObjectCache::alignToBin(size+headersSize+rml::internal::largeObjectAlignment);
101251c0b2f7Stbbdev     const int binIdx = defaultMemPool->extMemPool.loc.largeCache.sizeToIdx( allocationSize );
101351c0b2f7Stbbdev 
101451c0b2f7Stbbdev     CacheBinModel<rml::internal::LargeObjectCache::LargeCacheTypeProps>::cacheCurrTime = defaultMemPool->extMemPool.loc.cacheCurrTime;
101551c0b2f7Stbbdev     CacheBinModel<rml::internal::LargeObjectCache::LargeCacheTypeProps>::tooLargeLOC = defaultMemPool->extMemPool.loc.largeCache.tooLargeLOC;
101651c0b2f7Stbbdev     CacheBinModel<rml::internal::LargeObjectCache::LargeCacheTypeProps> cacheBinModel(defaultMemPool->extMemPool.loc.largeCache.bin[binIdx], allocationSize);
101751c0b2f7Stbbdev 
101851c0b2f7Stbbdev     Scenario scen;
101951c0b2f7Stbbdev     for (rml::internal::LargeMemoryBlock *lmb = scen.next(); (intptr_t)lmb != (intptr_t)-1; lmb = scen.next()) {
102051c0b2f7Stbbdev         if ( lmb ) {
102151c0b2f7Stbbdev             int num=1;
102251c0b2f7Stbbdev             for (rml::internal::LargeMemoryBlock *curr = lmb; curr->next; curr=curr->next) num+=1;
102351c0b2f7Stbbdev             defaultMemPool->extMemPool.freeLargeObject(lmb);
102451c0b2f7Stbbdev             cacheBinModel.putList(num);
102551c0b2f7Stbbdev         } else {
102651c0b2f7Stbbdev             scen.saveLmb(defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize));
102751c0b2f7Stbbdev             cacheBinModel.get();
102851c0b2f7Stbbdev         }
102951c0b2f7Stbbdev 
103051c0b2f7Stbbdev         cacheBinModel.check();
103151c0b2f7Stbbdev     }
103251c0b2f7Stbbdev }
103351c0b2f7Stbbdev 
103451c0b2f7Stbbdev class TestBootstrap {
103551c0b2f7Stbbdev     bool allocating;
103651c0b2f7Stbbdev     std::vector<rml::internal::LargeMemoryBlock*> lmbArray;
103751c0b2f7Stbbdev public:
103851c0b2f7Stbbdev     TestBootstrap() : allocating(true) {}
103951c0b2f7Stbbdev 
104051c0b2f7Stbbdev     rml::internal::LargeMemoryBlock* next() {
104151c0b2f7Stbbdev         if ( allocating )
104251c0b2f7Stbbdev             return NULL;
104351c0b2f7Stbbdev         if ( !lmbArray.empty() ) {
104451c0b2f7Stbbdev             rml::internal::LargeMemoryBlock *ret = lmbArray.back();
104551c0b2f7Stbbdev             lmbArray.pop_back();
104651c0b2f7Stbbdev             return ret;
104751c0b2f7Stbbdev         }
104851c0b2f7Stbbdev         return (rml::internal::LargeMemoryBlock*)-1;
104951c0b2f7Stbbdev     }
105051c0b2f7Stbbdev 
105151c0b2f7Stbbdev     void saveLmb( rml::internal::LargeMemoryBlock *lmb ) {
105251c0b2f7Stbbdev         lmb->next = NULL;
105351c0b2f7Stbbdev         lmbArray.push_back(lmb);
105451c0b2f7Stbbdev         if ( lmbArray.size() == 1000 ) allocating = false;
105551c0b2f7Stbbdev     }
105651c0b2f7Stbbdev };
105751c0b2f7Stbbdev 
105851c0b2f7Stbbdev class TestRandom {
105951c0b2f7Stbbdev     std::vector<rml::internal::LargeMemoryBlock*> lmbArray;
106051c0b2f7Stbbdev     int numOps;
106151c0b2f7Stbbdev public:
106251c0b2f7Stbbdev     TestRandom() : numOps(100000) {
106351c0b2f7Stbbdev         srand(1234);
106451c0b2f7Stbbdev     }
106551c0b2f7Stbbdev 
106651c0b2f7Stbbdev     rml::internal::LargeMemoryBlock* next() {
106751c0b2f7Stbbdev         if ( numOps-- ) {
106851c0b2f7Stbbdev             if ( lmbArray.empty() || rand() / (RAND_MAX>>1) == 0 )
106951c0b2f7Stbbdev                 return NULL;
107051c0b2f7Stbbdev             size_t ind = rand()%lmbArray.size();
107151c0b2f7Stbbdev             if ( ind != lmbArray.size()-1 ) std::swap(lmbArray[ind],lmbArray[lmbArray.size()-1]);
107251c0b2f7Stbbdev             rml::internal::LargeMemoryBlock *lmb = lmbArray.back();
107351c0b2f7Stbbdev             lmbArray.pop_back();
107451c0b2f7Stbbdev             return lmb;
107551c0b2f7Stbbdev         }
107651c0b2f7Stbbdev         return (rml::internal::LargeMemoryBlock*)-1;
107751c0b2f7Stbbdev     }
107851c0b2f7Stbbdev 
107951c0b2f7Stbbdev     void saveLmb( rml::internal::LargeMemoryBlock *lmb ) {
108051c0b2f7Stbbdev         lmb->next = NULL;
108151c0b2f7Stbbdev         lmbArray.push_back(lmb);
108251c0b2f7Stbbdev     }
108351c0b2f7Stbbdev };
108451c0b2f7Stbbdev 
108551c0b2f7Stbbdev class TestCollapsingMallocFree : public SimpleBarrier {
108651c0b2f7Stbbdev public:
108751c0b2f7Stbbdev     static const int NUM_ALLOCS = 100000;
108851c0b2f7Stbbdev     const int num_threads;
108951c0b2f7Stbbdev 
109051c0b2f7Stbbdev     TestCollapsingMallocFree( int _num_threads ) : num_threads(_num_threads) {
109151c0b2f7Stbbdev         initBarrier( num_threads );
109251c0b2f7Stbbdev     }
109351c0b2f7Stbbdev 
109451c0b2f7Stbbdev     void operator() ( int ) const {
109551c0b2f7Stbbdev         const size_t size = 16 * 1024;
109651c0b2f7Stbbdev         const size_t headersSize = sizeof(rml::internal::LargeMemoryBlock)+sizeof(rml::internal::LargeObjectHdr);
109751c0b2f7Stbbdev         const size_t allocationSize = LargeObjectCache::alignToBin(size+headersSize+rml::internal::largeObjectAlignment);
109851c0b2f7Stbbdev 
109951c0b2f7Stbbdev         barrier.wait();
110051c0b2f7Stbbdev         for ( int i=0; i<NUM_ALLOCS; ++i ) {
110151c0b2f7Stbbdev             defaultMemPool->extMemPool.freeLargeObject(
110251c0b2f7Stbbdev                 defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize) );
110351c0b2f7Stbbdev         }
110451c0b2f7Stbbdev     }
110551c0b2f7Stbbdev 
110651c0b2f7Stbbdev     void check() {
110751c0b2f7Stbbdev         REQUIRE( tbbmalloc_whitebox::locGetProcessed == tbbmalloc_whitebox::locPutProcessed);
110851c0b2f7Stbbdev         REQUIRE_MESSAGE( tbbmalloc_whitebox::locGetProcessed < num_threads*NUM_ALLOCS, "No one Malloc/Free pair was collapsed." );
110951c0b2f7Stbbdev     }
111051c0b2f7Stbbdev };
111151c0b2f7Stbbdev 
111251c0b2f7Stbbdev class TestCollapsingBootstrap : public SimpleBarrier {
111351c0b2f7Stbbdev     class CheckNumAllocs {
111451c0b2f7Stbbdev         const int num_threads;
111551c0b2f7Stbbdev     public:
111651c0b2f7Stbbdev         CheckNumAllocs( int _num_threads ) : num_threads(_num_threads) {}
111751c0b2f7Stbbdev         void operator()() const {
111851c0b2f7Stbbdev             REQUIRE( tbbmalloc_whitebox::locGetProcessed == num_threads*NUM_ALLOCS );
111951c0b2f7Stbbdev             REQUIRE( tbbmalloc_whitebox::locPutProcessed == 0 );
112051c0b2f7Stbbdev         }
112151c0b2f7Stbbdev     };
112251c0b2f7Stbbdev public:
112351c0b2f7Stbbdev     static const int NUM_ALLOCS = 1000;
112451c0b2f7Stbbdev     const int num_threads;
112551c0b2f7Stbbdev 
112651c0b2f7Stbbdev     TestCollapsingBootstrap( int _num_threads ) : num_threads(_num_threads) {
112751c0b2f7Stbbdev         initBarrier( num_threads );
112851c0b2f7Stbbdev     }
112951c0b2f7Stbbdev 
113051c0b2f7Stbbdev     void operator() ( int ) const {
113151c0b2f7Stbbdev         const size_t size = 16 * 1024;
113251c0b2f7Stbbdev         size_t headersSize = sizeof(rml::internal::LargeMemoryBlock)+sizeof(rml::internal::LargeObjectHdr);
113351c0b2f7Stbbdev         size_t allocationSize = LargeObjectCache::alignToBin(size+headersSize+rml::internal::largeObjectAlignment);
113451c0b2f7Stbbdev 
113551c0b2f7Stbbdev         barrier.wait();
113651c0b2f7Stbbdev         rml::internal::LargeMemoryBlock *lmbArray[NUM_ALLOCS];
113751c0b2f7Stbbdev         for ( int i=0; i<NUM_ALLOCS; ++i )
113851c0b2f7Stbbdev             lmbArray[i] = defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize);
113951c0b2f7Stbbdev 
114051c0b2f7Stbbdev         barrier.wait(CheckNumAllocs(num_threads));
114151c0b2f7Stbbdev         for ( int i=0; i<NUM_ALLOCS; ++i )
114251c0b2f7Stbbdev             defaultMemPool->extMemPool.freeLargeObject( lmbArray[i] );
114351c0b2f7Stbbdev     }
114451c0b2f7Stbbdev 
114551c0b2f7Stbbdev     void check() {
114651c0b2f7Stbbdev         REQUIRE( tbbmalloc_whitebox::locGetProcessed == tbbmalloc_whitebox::locPutProcessed );
114751c0b2f7Stbbdev         REQUIRE( tbbmalloc_whitebox::locGetProcessed == num_threads*NUM_ALLOCS );
114851c0b2f7Stbbdev     }
114951c0b2f7Stbbdev };
115051c0b2f7Stbbdev 
115151c0b2f7Stbbdev template <typename Scenario>
115251c0b2f7Stbbdev void LOCCollapsingTester( int num_threads ) {
115351c0b2f7Stbbdev     tbbmalloc_whitebox::locGetProcessed = 0;
115451c0b2f7Stbbdev     tbbmalloc_whitebox::locPutProcessed = 0;
115551c0b2f7Stbbdev     defaultMemPool->extMemPool.loc.cleanAll();
115651c0b2f7Stbbdev     defaultMemPool->extMemPool.loc.reset();
115751c0b2f7Stbbdev 
115851c0b2f7Stbbdev     Scenario scen(num_threads);
115951c0b2f7Stbbdev     utils::NativeParallelFor(num_threads, scen);
116051c0b2f7Stbbdev 
116151c0b2f7Stbbdev     scen.check();
116251c0b2f7Stbbdev }
116351c0b2f7Stbbdev 
116451c0b2f7Stbbdev void TestLOC() {
116551c0b2f7Stbbdev     LOCModelTester<TestBootstrap>();
116651c0b2f7Stbbdev     LOCModelTester<TestRandom>();
116751c0b2f7Stbbdev 
116851c0b2f7Stbbdev     const int num_threads = 16;
116951c0b2f7Stbbdev     LOCCollapsingTester<TestCollapsingBootstrap>( num_threads );
117051c0b2f7Stbbdev     if ( num_threads > 1 ) {
117151c0b2f7Stbbdev         INFO( "num_threads = " << num_threads );
117251c0b2f7Stbbdev         LOCCollapsingTester<TestCollapsingMallocFree>( num_threads );
117351c0b2f7Stbbdev     } else {
117451c0b2f7Stbbdev         REPORT( "Warning: concurrency is too low for TestMallocFreeCollapsing ( num_threads = %d )\n", num_threads );
117551c0b2f7Stbbdev     }
117651c0b2f7Stbbdev }
117751c0b2f7Stbbdev /*---------------------------------------------------------------------------*/
117851c0b2f7Stbbdev 
117951c0b2f7Stbbdev void *findCacheLine(void *p) {
118051c0b2f7Stbbdev     return (void*)alignDown((uintptr_t)p, estimatedCacheLineSize);
118151c0b2f7Stbbdev }
118251c0b2f7Stbbdev 
118351c0b2f7Stbbdev // test that internals of Block are at expected cache lines
118451c0b2f7Stbbdev void TestSlabAlignment() {
118551c0b2f7Stbbdev     const size_t min_sz = 8;
118651c0b2f7Stbbdev     const int space = 2*16*1024; // fill at least 2 slabs
118751c0b2f7Stbbdev     void *pointers[space / min_sz];  // the worst case is min_sz byte object
118851c0b2f7Stbbdev 
118951c0b2f7Stbbdev     for (size_t sz = min_sz; sz <= 64; sz *= 2) {
119051c0b2f7Stbbdev         for (size_t i = 0; i < space/sz; i++) {
119151c0b2f7Stbbdev             pointers[i] = scalable_malloc(sz);
119251c0b2f7Stbbdev             Block *block = (Block *)alignDown(pointers[i], slabSize);
119351c0b2f7Stbbdev             REQUIRE_MESSAGE(findCacheLine(&block->isFull) != findCacheLine(pointers[i]),
119451c0b2f7Stbbdev                           "A user object must not share a cache line with slab control structures.");
119551c0b2f7Stbbdev             REQUIRE_MESSAGE(findCacheLine(&block->next) != findCacheLine(&block->nextPrivatizable),
119651c0b2f7Stbbdev                           "GlobalBlockFields and LocalBlockFields must be on different cache lines.");
119751c0b2f7Stbbdev         }
119851c0b2f7Stbbdev         for (size_t i = 0; i < space/sz; i++)
119951c0b2f7Stbbdev             scalable_free(pointers[i]);
120051c0b2f7Stbbdev     }
120151c0b2f7Stbbdev }
120251c0b2f7Stbbdev 
120351c0b2f7Stbbdev #include "common/memory_usage.h"
120451c0b2f7Stbbdev 
120551c0b2f7Stbbdev // TODO: Consider adding Huge Pages support on macOS (special mmap flag).
120651c0b2f7Stbbdev // Transparent Huge pages support could be enabled by different system parsing mechanism,
120751c0b2f7Stbbdev // because there is no /proc/meminfo on macOS
120851c0b2f7Stbbdev #if __linux__
120951c0b2f7Stbbdev void TestTHP() {
121051c0b2f7Stbbdev     // Get backend from default memory pool
121151c0b2f7Stbbdev     rml::internal::Backend *backend = &(defaultMemPool->extMemPool.backend);
121251c0b2f7Stbbdev 
121351c0b2f7Stbbdev     // Configure malloc to use huge pages
121451c0b2f7Stbbdev     scalable_allocation_mode(USE_HUGE_PAGES, 1);
121551c0b2f7Stbbdev     REQUIRE_MESSAGE(hugePages.isEnabled, "Huge pages should be enabled via scalable_allocation_mode");
121651c0b2f7Stbbdev 
121751c0b2f7Stbbdev     const int HUGE_PAGE_SIZE = 2 * 1024 * 1024;
121851c0b2f7Stbbdev 
121951c0b2f7Stbbdev     // allocCount transparent huge pages should be allocated
122051c0b2f7Stbbdev     const int allocCount = 10;
122151c0b2f7Stbbdev 
122251c0b2f7Stbbdev     // Allocate huge page aligned memory regions to track system
122351c0b2f7Stbbdev     // counters for transparent huge pages
122451c0b2f7Stbbdev     void*  allocPtrs[allocCount];
122551c0b2f7Stbbdev 
122651c0b2f7Stbbdev     // Wait for the system to update process memory info files after other tests
122751c0b2f7Stbbdev     utils::Sleep(4000);
122851c0b2f7Stbbdev 
122951c0b2f7Stbbdev     // Parse system info regarding current THP status
123051c0b2f7Stbbdev     size_t currentSystemTHPCount = utils::getSystemTHPCount();
123151c0b2f7Stbbdev     size_t currentSystemTHPAllocatedSize = utils::getSystemTHPAllocatedSize();
123251c0b2f7Stbbdev 
123351c0b2f7Stbbdev     for (int i = 0; i < allocCount; i++) {
123451c0b2f7Stbbdev         // Allocation size have to be aligned on page size
123551c0b2f7Stbbdev         size_t allocSize = HUGE_PAGE_SIZE - (i * 1000);
123651c0b2f7Stbbdev 
123751c0b2f7Stbbdev         // Map memory
123851c0b2f7Stbbdev         allocPtrs[i] = backend->allocRawMem(allocSize);
123951c0b2f7Stbbdev 
124051c0b2f7Stbbdev         REQUIRE_MESSAGE(allocPtrs[i], "Allocation not succeeded.");
124151c0b2f7Stbbdev         REQUIRE_MESSAGE(allocSize == HUGE_PAGE_SIZE,
124251c0b2f7Stbbdev             "Allocation size have to be aligned on Huge Page size internally.");
124351c0b2f7Stbbdev 
124451c0b2f7Stbbdev         // First touch policy - no real pages allocated by OS without accessing the region
124551c0b2f7Stbbdev         memset(allocPtrs[i], 1, allocSize);
124651c0b2f7Stbbdev 
124751c0b2f7Stbbdev         REQUIRE_MESSAGE(isAligned(allocPtrs[i], HUGE_PAGE_SIZE),
124851c0b2f7Stbbdev             "The pointer returned by scalable_malloc is not aligned on huge page size.");
124951c0b2f7Stbbdev     }
125051c0b2f7Stbbdev 
125151c0b2f7Stbbdev     // Wait for the system to update process memory info files after allocations
125251c0b2f7Stbbdev     utils::Sleep(4000);
125351c0b2f7Stbbdev 
125451c0b2f7Stbbdev     // Generally, kernel tries to allocate transparent huge pages, but sometimes it cannot do this
125551c0b2f7Stbbdev     // (tested on SLES 11/12), so consider this system info checks as a remark.
125651c0b2f7Stbbdev     // Also, some systems can allocate more memory then needed in background (tested on Ubuntu 14.04)
125751c0b2f7Stbbdev     size_t newSystemTHPCount = utils::getSystemTHPCount();
125851c0b2f7Stbbdev     size_t newSystemTHPAllocatedSize = utils::getSystemTHPAllocatedSize();
125951c0b2f7Stbbdev     if ((newSystemTHPCount - currentSystemTHPCount) < allocCount
126051c0b2f7Stbbdev             && (newSystemTHPAllocatedSize - currentSystemTHPAllocatedSize) / (2 * 1024) < allocCount) {
126151c0b2f7Stbbdev         REPORT( "Warning: the system didn't allocate needed amount of THPs.\n" );
126251c0b2f7Stbbdev     }
126351c0b2f7Stbbdev 
126451c0b2f7Stbbdev     // Test memory unmap
126551c0b2f7Stbbdev     for (int i = 0; i < allocCount; i++) {
126651c0b2f7Stbbdev         REQUIRE_MESSAGE(backend->freeRawMem(allocPtrs[i], HUGE_PAGE_SIZE),
126751c0b2f7Stbbdev                 "Something went wrong during raw memory free");
126851c0b2f7Stbbdev     }
126951c0b2f7Stbbdev }
127051c0b2f7Stbbdev #endif // __linux__
127151c0b2f7Stbbdev 
127251c0b2f7Stbbdev inline size_t getStabilizedMemUsage() {
127351c0b2f7Stbbdev     for (int i = 0; i < 3; i++) utils::GetMemoryUsage();
127451c0b2f7Stbbdev     return utils::GetMemoryUsage();
127551c0b2f7Stbbdev }
127651c0b2f7Stbbdev 
127751c0b2f7Stbbdev inline void* reallocAndRetrieve(void* origPtr, size_t reallocSize, size_t& origBlockSize, size_t& reallocBlockSize) {
127851c0b2f7Stbbdev     rml::internal::LargeMemoryBlock* origLmb = ((rml::internal::LargeObjectHdr *)origPtr - 1)->memoryBlock;
127951c0b2f7Stbbdev     origBlockSize = origLmb->unalignedSize;
128051c0b2f7Stbbdev 
128151c0b2f7Stbbdev     void* reallocPtr = rml::internal::reallocAligned(defaultMemPool, origPtr, reallocSize, 0);
128251c0b2f7Stbbdev 
128351c0b2f7Stbbdev     // Retrieved reallocated block information
128451c0b2f7Stbbdev     rml::internal::LargeMemoryBlock* reallocLmb = ((rml::internal::LargeObjectHdr *)reallocPtr - 1)->memoryBlock;
128551c0b2f7Stbbdev     reallocBlockSize = reallocLmb->unalignedSize;
128651c0b2f7Stbbdev 
128751c0b2f7Stbbdev     return reallocPtr;
128851c0b2f7Stbbdev }
128951c0b2f7Stbbdev 
129051c0b2f7Stbbdev void TestReallocDecreasing() {
129151c0b2f7Stbbdev 
129251c0b2f7Stbbdev     /* Testing that actual reallocation happens for large objects that do not fit the backend cache
129351c0b2f7Stbbdev        but decrease in size by a factor of >= 2. */
129451c0b2f7Stbbdev 
129551c0b2f7Stbbdev     size_t startSize = 100 * 1024 * 1024;
129651c0b2f7Stbbdev     size_t maxBinnedSize = defaultMemPool->extMemPool.backend.getMaxBinnedSize();
129751c0b2f7Stbbdev     void*  origPtr = scalable_malloc(startSize);
129851c0b2f7Stbbdev     void*  reallocPtr = NULL;
129951c0b2f7Stbbdev 
130051c0b2f7Stbbdev     // Realloc on 1MB less size
130151c0b2f7Stbbdev     size_t origBlockSize = 42;
130251c0b2f7Stbbdev     size_t reallocBlockSize = 43;
130351c0b2f7Stbbdev     reallocPtr = reallocAndRetrieve(origPtr, startSize - 1 * 1024 * 1024, origBlockSize, reallocBlockSize);
130451c0b2f7Stbbdev     REQUIRE_MESSAGE(origBlockSize == reallocBlockSize, "Reallocated block size shouldn't change");
130551c0b2f7Stbbdev     REQUIRE_MESSAGE(reallocPtr == origPtr, "Original pointer shouldn't change");
130651c0b2f7Stbbdev 
130751c0b2f7Stbbdev     // Repeated decreasing reallocation while max cache bin size reached
130851c0b2f7Stbbdev     size_t reallocSize = (startSize / 2) - 1000; // exact realloc
130951c0b2f7Stbbdev     while(reallocSize > maxBinnedSize) {
131051c0b2f7Stbbdev 
131151c0b2f7Stbbdev         // Prevent huge/large objects caching
131251c0b2f7Stbbdev         defaultMemPool->extMemPool.loc.cleanAll();
131351c0b2f7Stbbdev         // Prevent local large object caching
131451c0b2f7Stbbdev         TLSData *tls = defaultMemPool->getTLS(/*create=*/false);
131551c0b2f7Stbbdev         tls->lloc.externalCleanup(&defaultMemPool->extMemPool);
131651c0b2f7Stbbdev 
131751c0b2f7Stbbdev         size_t sysMemUsageBefore = getStabilizedMemUsage();
131851c0b2f7Stbbdev         size_t totalMemSizeBefore = defaultMemPool->extMemPool.backend.getTotalMemSize();
131951c0b2f7Stbbdev 
132051c0b2f7Stbbdev         reallocPtr = reallocAndRetrieve(origPtr, reallocSize, origBlockSize, reallocBlockSize);
132151c0b2f7Stbbdev 
132251c0b2f7Stbbdev         REQUIRE_MESSAGE(origBlockSize > reallocBlockSize, "Reallocated block size should descrease.");
132351c0b2f7Stbbdev 
132451c0b2f7Stbbdev         size_t sysMemUsageAfter = getStabilizedMemUsage();
132551c0b2f7Stbbdev         size_t totalMemSizeAfter = defaultMemPool->extMemPool.backend.getTotalMemSize();
132651c0b2f7Stbbdev 
132751c0b2f7Stbbdev         // Prevent false checking when backend caching occurred or could not read system memory usage info
132851c0b2f7Stbbdev         if (totalMemSizeBefore > totalMemSizeAfter && sysMemUsageAfter != 0 && sysMemUsageBefore != 0) {
132951c0b2f7Stbbdev             REQUIRE_MESSAGE(sysMemUsageBefore > sysMemUsageAfter, "Memory were not released");
133051c0b2f7Stbbdev         }
133151c0b2f7Stbbdev 
133251c0b2f7Stbbdev         origPtr = reallocPtr;
133351c0b2f7Stbbdev         reallocSize = (reallocSize / 2) - 1000; // exact realloc
133451c0b2f7Stbbdev     }
133551c0b2f7Stbbdev     scalable_free(reallocPtr);
133651c0b2f7Stbbdev 
133751c0b2f7Stbbdev     /* TODO: Decreasing reallocation of large objects that fit backend cache */
133851c0b2f7Stbbdev     /* TODO: Small objects decreasing reallocation test */
133951c0b2f7Stbbdev }
134051c0b2f7Stbbdev #if !__TBB_WIN8UI_SUPPORT && defined(_WIN32)
134151c0b2f7Stbbdev 
134251c0b2f7Stbbdev #include "../../src/tbbmalloc_proxy/function_replacement.cpp"
134351c0b2f7Stbbdev #include <string>
134451c0b2f7Stbbdev namespace FunctionReplacement {
134551c0b2f7Stbbdev     FunctionInfo funcInfo = { "funcname","dllname" };
134651c0b2f7Stbbdev     char **func_replacement_log;
134751c0b2f7Stbbdev     int status;
134851c0b2f7Stbbdev 
134951c0b2f7Stbbdev     void LogCleanup() {
135051c0b2f7Stbbdev         // Free all allocated memory
135151c0b2f7Stbbdev         for (unsigned i = 0; i < Log::record_number; i++){
135251c0b2f7Stbbdev             HeapFree(GetProcessHeap(), 0, Log::records[i]);
135351c0b2f7Stbbdev         }
135451c0b2f7Stbbdev         for (unsigned i = 0; i < Log::RECORDS_COUNT + 1; i++){
135551c0b2f7Stbbdev             Log::records[i] = NULL;
135651c0b2f7Stbbdev         }
135751c0b2f7Stbbdev         Log::replacement_status = true;
135851c0b2f7Stbbdev         Log::record_number = 0;
135951c0b2f7Stbbdev     }
136051c0b2f7Stbbdev 
136151c0b2f7Stbbdev     void TestEmptyLog() {
136251c0b2f7Stbbdev         status = TBB_malloc_replacement_log(&func_replacement_log);
136351c0b2f7Stbbdev 
136451c0b2f7Stbbdev         REQUIRE_MESSAGE(status == -1, "Status is true, but log is empty");
136551c0b2f7Stbbdev         REQUIRE_MESSAGE(*func_replacement_log == nullptr, "Log must be empty");
136651c0b2f7Stbbdev     }
136751c0b2f7Stbbdev 
136851c0b2f7Stbbdev     void TestLogOverload() {
136951c0b2f7Stbbdev         for (int i = 0; i < 1000; i++)
137051c0b2f7Stbbdev             Log::record(funcInfo, "opcode string", true);
137151c0b2f7Stbbdev 
137251c0b2f7Stbbdev         status = TBB_malloc_replacement_log(&func_replacement_log);
137351c0b2f7Stbbdev         // Find last record
137451c0b2f7Stbbdev         for (; *(func_replacement_log + 1) != 0; func_replacement_log++) {}
137551c0b2f7Stbbdev 
137651c0b2f7Stbbdev         std::string last_line(*func_replacement_log);
137751c0b2f7Stbbdev         REQUIRE_MESSAGE(status == 0, "False status, but all functions found");
137851c0b2f7Stbbdev         REQUIRE_MESSAGE(last_line.compare("Log was truncated.") == 0, "Log overflow was not handled");
137951c0b2f7Stbbdev 
138051c0b2f7Stbbdev         // Change status
138151c0b2f7Stbbdev         Log::record(funcInfo, "opcode string", false);
138251c0b2f7Stbbdev         status = TBB_malloc_replacement_log(NULL);
138351c0b2f7Stbbdev         REQUIRE_MESSAGE(status == -1, "Status is true, but we have false search case");
138451c0b2f7Stbbdev 
138551c0b2f7Stbbdev         LogCleanup();
138651c0b2f7Stbbdev     }
138751c0b2f7Stbbdev 
138851c0b2f7Stbbdev     void TestFalseSearchCase() {
138951c0b2f7Stbbdev         Log::record(funcInfo, "opcode string", false);
139051c0b2f7Stbbdev         std::string expected_line = "Fail: "+ std::string(funcInfo.funcName) + " (" +
139151c0b2f7Stbbdev                          std::string(funcInfo.dllName) + "), byte pattern: <opcode string>";
139251c0b2f7Stbbdev 
139351c0b2f7Stbbdev         status = TBB_malloc_replacement_log(&func_replacement_log);
139451c0b2f7Stbbdev 
139551c0b2f7Stbbdev         REQUIRE_MESSAGE(expected_line.compare(*func_replacement_log) == 0, "Wrong last string contnent");
139651c0b2f7Stbbdev         REQUIRE_MESSAGE(status == -1, "Status is true, but we have false search case");
139751c0b2f7Stbbdev         LogCleanup();
139851c0b2f7Stbbdev     }
139951c0b2f7Stbbdev 
140051c0b2f7Stbbdev     void TestWrongFunctionInDll(){
140151c0b2f7Stbbdev         HMODULE ucrtbase_handle = GetModuleHandle("ucrtbase.dll");
140251c0b2f7Stbbdev         if (ucrtbase_handle) {
140351c0b2f7Stbbdev             IsPrologueKnown("ucrtbase.dll", "fake_function", NULL, ucrtbase_handle);
140451c0b2f7Stbbdev             std::string expected_line = "Fail: fake_function (ucrtbase.dll), byte pattern: <unknown>";
140551c0b2f7Stbbdev 
140651c0b2f7Stbbdev             status = TBB_malloc_replacement_log(&func_replacement_log);
140751c0b2f7Stbbdev 
140851c0b2f7Stbbdev             REQUIRE_MESSAGE(expected_line.compare(*func_replacement_log) == 0, "Wrong last string contnent");
140951c0b2f7Stbbdev             REQUIRE_MESSAGE(status == -1, "Status is true, but we have false search case");
141051c0b2f7Stbbdev             LogCleanup();
141151c0b2f7Stbbdev         } else {
141251c0b2f7Stbbdev             INFO("Cannot found ucrtbase.dll on system, test skipped!\n");
141351c0b2f7Stbbdev         }
141451c0b2f7Stbbdev     }
141551c0b2f7Stbbdev }
141651c0b2f7Stbbdev 
141751c0b2f7Stbbdev void TesFunctionReplacementLog() {
141851c0b2f7Stbbdev     using namespace FunctionReplacement;
141951c0b2f7Stbbdev     // Do not reorder the test cases
142051c0b2f7Stbbdev     TestEmptyLog();
142151c0b2f7Stbbdev     TestLogOverload();
142251c0b2f7Stbbdev     TestFalseSearchCase();
142351c0b2f7Stbbdev     TestWrongFunctionInDll();
142451c0b2f7Stbbdev }
142551c0b2f7Stbbdev 
142651c0b2f7Stbbdev #endif /*!__TBB_WIN8UI_SUPPORT && defined(_WIN32)*/
142751c0b2f7Stbbdev 
142851c0b2f7Stbbdev #include <cmath> // pow function
142951c0b2f7Stbbdev 
143051c0b2f7Stbbdev // Huge objects cache: Size = MinSize * (2 ^ (Index / StepFactor) formula gives value for the bin size,
143151c0b2f7Stbbdev // but it is not matched with our sizeToIdx approximation algorithm, where step sizes between major
143251c0b2f7Stbbdev // (power of 2) sizes are equal. Used internally for the test. Static cast to avoid warnings.
143351c0b2f7Stbbdev inline size_t hocIdxToSizeFormula(int idx) {
143451c0b2f7Stbbdev     return static_cast<size_t>(float(rml::internal::LargeObjectCache::maxLargeSize) *
143551c0b2f7Stbbdev         pow(2, float(idx) / float(rml::internal::LargeObjectCache::HugeBSProps::StepFactor)));
143651c0b2f7Stbbdev }
143751c0b2f7Stbbdev // Large objects cache arithmetic progression
143851c0b2f7Stbbdev inline size_t locIdxToSizeFormula(int idx) {
143951c0b2f7Stbbdev     return rml::internal::LargeObjectCache::LargeBSProps::MinSize +
144051c0b2f7Stbbdev         (idx * rml::internal::LargeObjectCache::LargeBSProps::CacheStep);
144151c0b2f7Stbbdev }
144251c0b2f7Stbbdev 
144351c0b2f7Stbbdev template <typename CacheType>
144451c0b2f7Stbbdev void TestLOCacheBinsConverterImpl(int idx, size_t checkingSize) {
144551c0b2f7Stbbdev     size_t alignedSize = CacheType::alignToBin(checkingSize);
144651c0b2f7Stbbdev     REQUIRE_MESSAGE(alignedSize >= checkingSize, "Size is not correctly aligned");
144751c0b2f7Stbbdev     int calcIdx = CacheType::sizeToIdx(alignedSize);
144851c0b2f7Stbbdev     REQUIRE_MESSAGE(calcIdx == idx, "Index from size calculated not correctly");
144951c0b2f7Stbbdev }
145051c0b2f7Stbbdev 
145151c0b2f7Stbbdev void TestLOCacheBinsConverter(){
145251c0b2f7Stbbdev     typedef rml::internal::LargeObjectCache::LargeCacheType LargeCacheType;
145351c0b2f7Stbbdev     typedef rml::internal::LargeObjectCache::HugeCacheType HugeCacheType;
145451c0b2f7Stbbdev 
145551c0b2f7Stbbdev     size_t checkingSize = 0;
145651c0b2f7Stbbdev     for (int idx = 0; idx < LargeCacheType::numBins; idx++) {
145751c0b2f7Stbbdev         checkingSize = locIdxToSizeFormula(idx);
145851c0b2f7Stbbdev         TestLOCacheBinsConverterImpl<LargeCacheType>(idx, checkingSize);
145951c0b2f7Stbbdev     }
146051c0b2f7Stbbdev     for (int idx = 0; idx < HugeCacheType::numBins; idx++) {
146151c0b2f7Stbbdev         checkingSize = hocIdxToSizeFormula(idx);
146251c0b2f7Stbbdev         TestLOCacheBinsConverterImpl<HugeCacheType>(idx, checkingSize);
146351c0b2f7Stbbdev     }
146451c0b2f7Stbbdev }
146551c0b2f7Stbbdev 
146651c0b2f7Stbbdev struct HOThresholdTester {
146751c0b2f7Stbbdev     LargeObjectCache* loc;
146851c0b2f7Stbbdev     size_t hugeSize;
146951c0b2f7Stbbdev 
147051c0b2f7Stbbdev     static const size_t sieveSize = LargeObjectCache::defaultMaxHugeSize;
147151c0b2f7Stbbdev     // Sieve starts from 64MB (24-th cache bin), enough to check 4 bins radius range
147251c0b2f7Stbbdev     // for decent memory consumption (especially for 32-bit arch)
147351c0b2f7Stbbdev     static const int MIN_BIN_IDX = 21;
147451c0b2f7Stbbdev     static const int MAX_BIN_IDX = 27;
147551c0b2f7Stbbdev 
147651c0b2f7Stbbdev     enum CleanupType {
147751c0b2f7Stbbdev         NO_CLEANUP,
147851c0b2f7Stbbdev         REGULAR_CLEANUP,
147951c0b2f7Stbbdev         HARD_CLEANUP
148051c0b2f7Stbbdev     };
148151c0b2f7Stbbdev 
148251c0b2f7Stbbdev     void populateCache() {
148351c0b2f7Stbbdev         LargeMemoryBlock* loArray[MAX_BIN_IDX - MIN_BIN_IDX];
148451c0b2f7Stbbdev         // To avoid backend::softCacheCleanup consequences (cleanup by isLOCToolarge),
148551c0b2f7Stbbdev         // firstly allocate all objects and then cache them at once.
148651c0b2f7Stbbdev         // Morevover, just because first cache item will still be dropped from cache because of the lack of history,
148751c0b2f7Stbbdev         // redo allocation 2 times.
148851c0b2f7Stbbdev         for (int idx = MIN_BIN_IDX; idx < MAX_BIN_IDX; ++idx) {
148951c0b2f7Stbbdev             size_t allocationSize = alignedSizeFromIdx(idx);
149051c0b2f7Stbbdev             int localIdx = idx - MIN_BIN_IDX;
149151c0b2f7Stbbdev             loArray[localIdx] = defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize);
149251c0b2f7Stbbdev             REQUIRE_MESSAGE(loArray[localIdx], "Large object was not allocated.");
149351c0b2f7Stbbdev             loc->put(loArray[localIdx]);
149451c0b2f7Stbbdev             loArray[localIdx] = defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize);
149551c0b2f7Stbbdev         }
149651c0b2f7Stbbdev         for (int idx = MIN_BIN_IDX; idx < MAX_BIN_IDX; ++idx) {
149751c0b2f7Stbbdev             loc->put(loArray[idx - MIN_BIN_IDX]);
149851c0b2f7Stbbdev         }
149951c0b2f7Stbbdev     }
150051c0b2f7Stbbdev     void clean(bool all) {
150151c0b2f7Stbbdev         if (all) {
150251c0b2f7Stbbdev             // Should avoid any threshold and clean all bins
150351c0b2f7Stbbdev             loc->cleanAll();
150451c0b2f7Stbbdev         } else {
150551c0b2f7Stbbdev             // Regular cleanup should do nothing for bins above threshold. Decreasing option used
150651c0b2f7Stbbdev             // for the test to be sure that all objects below defaultMaxHugeSize (sieveSize) were cleaned
150751c0b2f7Stbbdev             loc->regularCleanup();
150851c0b2f7Stbbdev             loc->decreasingCleanup();
150951c0b2f7Stbbdev         }
151051c0b2f7Stbbdev     }
151151c0b2f7Stbbdev     void check(CleanupType type) {
151251c0b2f7Stbbdev         for (int idx = MIN_BIN_IDX; idx < MAX_BIN_IDX; ++idx) {
151351c0b2f7Stbbdev             size_t objectSize = alignedSizeFromIdx(idx);
151451c0b2f7Stbbdev             // Cache object below sieve threshold and above huge object threshold should be cached
151551c0b2f7Stbbdev             // (other should be sieved). Unless all cache is dropped. Regular cleanup drops object only below sieve size.
151651c0b2f7Stbbdev             if (type == NO_CLEANUP && sizeInCacheRange(objectSize)) {
151751c0b2f7Stbbdev                 REQUIRE_MESSAGE(objectInCacheBin(idx, objectSize), "Object was released from cache, it shouldn't.");
151851c0b2f7Stbbdev             } else if (type == REGULAR_CLEANUP && (objectSize >= hugeSize)) {
151951c0b2f7Stbbdev                 REQUIRE_MESSAGE(objectInCacheBin(idx, objectSize), "Object was released from cache, it shouldn't.");
152051c0b2f7Stbbdev             } else { // HARD_CLEANUP
152151c0b2f7Stbbdev                 REQUIRE_MESSAGE(cacheBinEmpty(idx), "Object is still cached.");
152251c0b2f7Stbbdev             }
152351c0b2f7Stbbdev         }
152451c0b2f7Stbbdev     }
152551c0b2f7Stbbdev 
152651c0b2f7Stbbdev private:
152751c0b2f7Stbbdev     bool cacheBinEmpty(int idx) {
152851c0b2f7Stbbdev         return (loc->hugeCache.bin[idx].cachedSize == 0 && loc->hugeCache.bin[idx].get() == NULL);
152951c0b2f7Stbbdev     }
153051c0b2f7Stbbdev     bool objectInCacheBin(int idx, size_t size) {
153151c0b2f7Stbbdev         return (loc->hugeCache.bin[idx].cachedSize != 0 && loc->hugeCache.bin[idx].cachedSize % size == 0);
153251c0b2f7Stbbdev     }
153351c0b2f7Stbbdev     bool sizeInCacheRange(size_t size) {
153451c0b2f7Stbbdev         return size <= sieveSize || size >= hugeSize;
153551c0b2f7Stbbdev     }
153651c0b2f7Stbbdev     size_t alignedSizeFromIdx(int idx) {
153751c0b2f7Stbbdev         return rml::internal::LargeObjectCache::alignToBin(hocIdxToSizeFormula(idx));
153851c0b2f7Stbbdev     }
153951c0b2f7Stbbdev };
154051c0b2f7Stbbdev 
154151c0b2f7Stbbdev // TBBMALLOC_SET_HUGE_OBJECT_THRESHOLD value should be set before the test,
154251c0b2f7Stbbdev // through scalable API or env variable
154351c0b2f7Stbbdev void TestHugeSizeThresholdImpl(LargeObjectCache* loc, size_t hugeSize, bool fullTesting) {
154451c0b2f7Stbbdev     HOThresholdTester test = {loc, hugeSize};
154551c0b2f7Stbbdev     test.populateCache();
154651c0b2f7Stbbdev     // Check the default sieve value
154751c0b2f7Stbbdev     test.check(HOThresholdTester::NO_CLEANUP);
154851c0b2f7Stbbdev 
154951c0b2f7Stbbdev     if(fullTesting) {
155051c0b2f7Stbbdev         // Check that objects above threshold stay in cache after regular cleanup
155151c0b2f7Stbbdev         test.clean(/*all*/false);
155251c0b2f7Stbbdev         test.check(HOThresholdTester::REGULAR_CLEANUP);
155351c0b2f7Stbbdev     }
155451c0b2f7Stbbdev     // Check that all objects dropped from cache after hard cleanup (ignore huge obects threshold)
155551c0b2f7Stbbdev     test.clean(/*all*/true);
155651c0b2f7Stbbdev     test.check(HOThresholdTester::HARD_CLEANUP);
155751c0b2f7Stbbdev     // Restore previous settings
155851c0b2f7Stbbdev     loc->setHugeSizeThreshold(LargeObjectCache::maxHugeSize);
155951c0b2f7Stbbdev     loc->reset();
156051c0b2f7Stbbdev }
156151c0b2f7Stbbdev 
156251c0b2f7Stbbdev /*
156351c0b2f7Stbbdev  *  Test for default huge size and behaviour when huge object settings defined
156451c0b2f7Stbbdev  */
156551c0b2f7Stbbdev void TestHugeSizeThreshold() {
156651c0b2f7Stbbdev     // Clean up if something was allocated before the test and reset cache state
156751c0b2f7Stbbdev     scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS, 0);
156851c0b2f7Stbbdev     LargeObjectCache* loc = &defaultMemPool->extMemPool.loc;
156951c0b2f7Stbbdev     // Restore default settings just in case
157051c0b2f7Stbbdev     loc->setHugeSizeThreshold(LargeObjectCache::maxHugeSize);
157151c0b2f7Stbbdev     loc->reset();
157251c0b2f7Stbbdev     // Firstly check default huge size value (with max huge object threshold).
157351c0b2f7Stbbdev     // Everything that more then this value should be released to OS without caching.
157451c0b2f7Stbbdev     TestHugeSizeThresholdImpl(loc, loc->hugeSizeThreshold, false);
157551c0b2f7Stbbdev     // Then set huge object threshold.
157651c0b2f7Stbbdev     // All objects with sizes after threshold will be released only after the hard cleanup.
157751c0b2f7Stbbdev #if !__TBB_WIN8UI_SUPPORT
157851c0b2f7Stbbdev     // Unit testing for environment variable
157951c0b2f7Stbbdev     utils::SetEnv("TBB_MALLOC_SET_HUGE_SIZE_THRESHOLD","67108864");
158051c0b2f7Stbbdev     // Large object cache reads threshold environment during initialization.
158151c0b2f7Stbbdev     // Reset the value before the test.
158251c0b2f7Stbbdev     loc->hugeSizeThreshold = 0;
158351c0b2f7Stbbdev     loc->init(&defaultMemPool->extMemPool);
158451c0b2f7Stbbdev     TestHugeSizeThresholdImpl(loc, 64 * MByte, true);
158551c0b2f7Stbbdev #endif
158651c0b2f7Stbbdev     // Unit testing for scalable_allocation_command
158751c0b2f7Stbbdev     scalable_allocation_mode(TBBMALLOC_SET_HUGE_SIZE_THRESHOLD, 56 * MByte);
158851c0b2f7Stbbdev     TestHugeSizeThresholdImpl(loc, 56 * MByte, true);
158951c0b2f7Stbbdev }
159051c0b2f7Stbbdev 
159151c0b2f7Stbbdev //! \brief \ref error_guessing
159251c0b2f7Stbbdev TEST_CASE("Main test case") {
159351c0b2f7Stbbdev     scalable_allocation_mode(USE_HUGE_PAGES, 0);
159451c0b2f7Stbbdev #if !__TBB_WIN8UI_SUPPORT
159551c0b2f7Stbbdev     utils::SetEnv("TBB_MALLOC_USE_HUGE_PAGES","yes");
159651c0b2f7Stbbdev #endif
159751c0b2f7Stbbdev     checkNoHugePages();
159851c0b2f7Stbbdev     // backreference requires that initialization was done
159951c0b2f7Stbbdev     if(!isMallocInitialized()) doInitialization();
160051c0b2f7Stbbdev     checkNoHugePages();
160151c0b2f7Stbbdev     // to succeed, leak detection must be the 1st memory-intensive test
160251c0b2f7Stbbdev     TestBackRef();
160351c0b2f7Stbbdev     TestCleanAllBuffers<4*1024>();
160451c0b2f7Stbbdev     TestCleanAllBuffers<16*1024>();
160551c0b2f7Stbbdev     TestCleanThreadBuffers();
160651c0b2f7Stbbdev     TestPools();
160751c0b2f7Stbbdev     TestBackend();
160851c0b2f7Stbbdev 
160951c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION
161051c0b2f7Stbbdev     for( int p=MaxThread; p>=MinThread; --p ) {
161151c0b2f7Stbbdev         TestStartupAlloc::initBarrier( p );
161251c0b2f7Stbbdev         utils::NativeParallelFor( p, TestStartupAlloc() );
161351c0b2f7Stbbdev         REQUIRE_MESSAGE(!firstStartupBlock, "Startup heap memory leak detected");
161451c0b2f7Stbbdev     }
161551c0b2f7Stbbdev #endif
161651c0b2f7Stbbdev     TestLargeObjectCache();
161751c0b2f7Stbbdev     TestObjectRecognition();
161851c0b2f7Stbbdev     TestBitMask();
161951c0b2f7Stbbdev     TestHeapLimit();
162051c0b2f7Stbbdev     TestLOC();
162151c0b2f7Stbbdev     TestSlabAlignment();
162251c0b2f7Stbbdev }
162351c0b2f7Stbbdev 
162451c0b2f7Stbbdev //! \brief \ref error_guessing
162551c0b2f7Stbbdev TEST_CASE("Decreasing reallocation") {
162651c0b2f7Stbbdev     if (!isMallocInitialized()) doInitialization();
162751c0b2f7Stbbdev     TestReallocDecreasing();
162851c0b2f7Stbbdev }
162951c0b2f7Stbbdev 
163051c0b2f7Stbbdev //! \brief \ref error_guessing
163151c0b2f7Stbbdev TEST_CASE("Large object cache bins converter") {
163251c0b2f7Stbbdev     if (!isMallocInitialized()) doInitialization();
163351c0b2f7Stbbdev     TestLOCacheBinsConverter();
163451c0b2f7Stbbdev }
163551c0b2f7Stbbdev 
163651c0b2f7Stbbdev //! \brief \ref error_guessing
163751c0b2f7Stbbdev TEST_CASE("Huge size threshold settings") {
163851c0b2f7Stbbdev     if (!isMallocInitialized()) doInitialization();
163951c0b2f7Stbbdev     TestHugeSizeThreshold();
164051c0b2f7Stbbdev }
164151c0b2f7Stbbdev 
164251c0b2f7Stbbdev #if __linux__
164351c0b2f7Stbbdev //! \brief \ref error_guessing
164451c0b2f7Stbbdev TEST_CASE("Transparent huge pages") {
164551c0b2f7Stbbdev     if (utils::isTHPEnabledOnMachine()) {
164651c0b2f7Stbbdev         if (!isMallocInitialized()) doInitialization();
164751c0b2f7Stbbdev         TestTHP();
164851c0b2f7Stbbdev     } else {
164951c0b2f7Stbbdev         INFO("Transparent Huge Pages is not supported on the system - skipped the test\n");
165051c0b2f7Stbbdev     }
165151c0b2f7Stbbdev }
165251c0b2f7Stbbdev #endif
165351c0b2f7Stbbdev 
165451c0b2f7Stbbdev #if !__TBB_WIN8UI_SUPPORT && defined(_WIN32)
165551c0b2f7Stbbdev //! \brief \ref error_guessing
165651c0b2f7Stbbdev TEST_CASE("Function replacement log") {
165751c0b2f7Stbbdev     TesFunctionReplacementLog();
165851c0b2f7Stbbdev }
165951c0b2f7Stbbdev #endif
1660