151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev     Copyright (c) 2005-2021 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 
4049e08aacStbbdev #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;
7958dcbd5b1Stbbdev static thread_local bool free_was_called = false;
79651c0b2f7Stbbdev 
79751c0b2f7Stbbdev inline void multiThreadAlloc(size_t alloc_size) {
79851c0b2f7Stbbdev     for( int i = alloc_counter++; i < num_allocs; i = alloc_counter++ ) {
79951c0b2f7Stbbdev        ptrs[i] = scalable_malloc( alloc_size );
80051c0b2f7Stbbdev        REQUIRE_MESSAGE( ptrs[i] != nullptr, "scalable_malloc returned zero." );
80151c0b2f7Stbbdev     }
80251c0b2f7Stbbdev }
80351c0b2f7Stbbdev inline void crossThreadDealloc() {
8048dcbd5b1Stbbdev     free_was_called = false;
80551c0b2f7Stbbdev     for( int i = --alloc_counter; i >= 0; i = --alloc_counter ) {
8068dcbd5b1Stbbdev         if (i < num_allocs) {
8078dcbd5b1Stbbdev             scalable_free(ptrs[i]);
8088dcbd5b1Stbbdev             free_was_called = true;
8098dcbd5b1Stbbdev         }
81051c0b2f7Stbbdev     }
81151c0b2f7Stbbdev }
81251c0b2f7Stbbdev 
81351c0b2f7Stbbdev template<int AllocSize>
81451c0b2f7Stbbdev struct TestCleanAllBuffersBody : public SimpleBarrier {
81551c0b2f7Stbbdev     void operator() ( int ) const {
81651c0b2f7Stbbdev         barrier.wait();
81751c0b2f7Stbbdev         multiThreadAlloc(AllocSize);
81851c0b2f7Stbbdev         barrier.wait();
81951c0b2f7Stbbdev         crossThreadDealloc();
82051c0b2f7Stbbdev     }
82151c0b2f7Stbbdev };
82251c0b2f7Stbbdev 
82351c0b2f7Stbbdev template<int AllocSize>
82451c0b2f7Stbbdev void TestCleanAllBuffers() {
82551c0b2f7Stbbdev     const int num_threads = 8;
82651c0b2f7Stbbdev     // Clean up if something was allocated before the test
82751c0b2f7Stbbdev     scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS,0);
82851c0b2f7Stbbdev 
82951c0b2f7Stbbdev     size_t memory_in_use_before = getMemSize();
83051c0b2f7Stbbdev     alloc_counter = 0;
83151c0b2f7Stbbdev     TestCleanAllBuffersBody<AllocSize>::initBarrier(num_threads);
83251c0b2f7Stbbdev 
83351c0b2f7Stbbdev     utils::NativeParallelFor(num_threads, TestCleanAllBuffersBody<AllocSize>());
83451c0b2f7Stbbdev     // TODO: reproduce the bug conditions more reliably
83551c0b2f7Stbbdev     if ( defaultMemPool->extMemPool.backend.coalescQ.blocksToFree.load(std::memory_order_relaxed) == NULL ) {
83651c0b2f7Stbbdev         INFO( "Warning: The queue of postponed coalescing requests is empty. ");
83751c0b2f7Stbbdev         INFO( "Unable to create the condition for bug reproduction.\n" );
83851c0b2f7Stbbdev     }
83951c0b2f7Stbbdev     int result = scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS,0);
84051c0b2f7Stbbdev     REQUIRE_MESSAGE( result == TBBMALLOC_OK, "The cleanup request has not cleaned anything." );
84151c0b2f7Stbbdev     size_t memory_in_use_after = getMemSize();
84251c0b2f7Stbbdev 
84351c0b2f7Stbbdev     size_t memory_leak = memory_in_use_after - memory_in_use_before;
84451c0b2f7Stbbdev     INFO( "memory_in_use_before = " <<  memory_in_use_before << ", memory_in_use_after = " << memory_in_use_after << "\n" );
84551c0b2f7Stbbdev     REQUIRE_MESSAGE( memory_leak == 0, "Cleanup was unable to release all allocated memory." );
84651c0b2f7Stbbdev }
84751c0b2f7Stbbdev 
84851c0b2f7Stbbdev //! Force cross thread deallocation of small objects to create a set of privatizable slab blocks.
84951c0b2f7Stbbdev //! TBBMALLOC_CLEAN_THREAD_BUFFERS command have to privatize all the block.
85051c0b2f7Stbbdev struct TestCleanThreadBuffersBody : public SimpleBarrier {
85151c0b2f7Stbbdev     void operator() ( int ) const {
85251c0b2f7Stbbdev         barrier.wait();
85351c0b2f7Stbbdev         multiThreadAlloc(2*1024);
85451c0b2f7Stbbdev         barrier.wait();
85551c0b2f7Stbbdev         crossThreadDealloc();
85651c0b2f7Stbbdev         barrier.wait();
85751c0b2f7Stbbdev         int result = scalable_allocation_command(TBBMALLOC_CLEAN_THREAD_BUFFERS,0);
8588dcbd5b1Stbbdev         if (result != TBBMALLOC_OK && free_was_called) {
85951c0b2f7Stbbdev             REPORT("Warning: clean-up request for this particular thread has not cleaned anything.");
86051c0b2f7Stbbdev         }
86151c0b2f7Stbbdev 
86251c0b2f7Stbbdev         // Check that TLS was cleaned fully
86351c0b2f7Stbbdev         TLSData *tlsCurr = defaultMemPool->getTLS(/*create=*/false);
8648dcbd5b1Stbbdev         if (tlsCurr) {
86551c0b2f7Stbbdev             for (int i = 0; i < numBlockBinLimit; i++) {
86651c0b2f7Stbbdev                 REQUIRE_MESSAGE(!(tlsCurr->bin[i].activeBlk), "Some bin was not cleaned.");
86751c0b2f7Stbbdev             }
86851c0b2f7Stbbdev             REQUIRE_MESSAGE(!(tlsCurr->lloc.head.load(std::memory_order_relaxed)), "Local LOC was not cleaned.");
86951c0b2f7Stbbdev             REQUIRE_MESSAGE(!(tlsCurr->freeSlabBlocks.head.load(std::memory_order_relaxed)), "Free Block pool was not cleaned.");
87051c0b2f7Stbbdev         }
8718dcbd5b1Stbbdev     }
87251c0b2f7Stbbdev };
87351c0b2f7Stbbdev 
87451c0b2f7Stbbdev void TestCleanThreadBuffers() {
87551c0b2f7Stbbdev     const int num_threads = 8;
87651c0b2f7Stbbdev     // Clean up if something was allocated before the test
87751c0b2f7Stbbdev     scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS,0);
87851c0b2f7Stbbdev 
87951c0b2f7Stbbdev     alloc_counter = 0;
88051c0b2f7Stbbdev     TestCleanThreadBuffersBody::initBarrier(num_threads);
88151c0b2f7Stbbdev     utils::NativeParallelFor(num_threads, TestCleanThreadBuffersBody());
88251c0b2f7Stbbdev }
88351c0b2f7Stbbdev 
88451c0b2f7Stbbdev /*---------------------------------------------------------------------------*/
88551c0b2f7Stbbdev /*------------------------- Large Object Cache tests ------------------------*/
88651c0b2f7Stbbdev #if _MSC_VER==1600 || _MSC_VER==1500
88751c0b2f7Stbbdev     // ignore C4275: non dll-interface class 'stdext::exception' used as
88851c0b2f7Stbbdev     // base for dll-interface class 'std::bad_cast'
88951c0b2f7Stbbdev     #pragma warning (disable: 4275)
89051c0b2f7Stbbdev #endif
89151c0b2f7Stbbdev #include <vector>
89251c0b2f7Stbbdev #include <list>
89351c0b2f7Stbbdev 
89451c0b2f7Stbbdev // default constructor of CacheBin
89551c0b2f7Stbbdev template<typename Props>
89651c0b2f7Stbbdev rml::internal::LargeObjectCacheImpl<Props>::CacheBin::CacheBin() {}
89751c0b2f7Stbbdev 
89851c0b2f7Stbbdev template<typename Props>
89951c0b2f7Stbbdev class CacheBinModel {
90051c0b2f7Stbbdev 
90151c0b2f7Stbbdev     typedef typename rml::internal::LargeObjectCacheImpl<Props>::CacheBin CacheBinType;
90251c0b2f7Stbbdev 
90351c0b2f7Stbbdev     // The emulated cache bin.
90451c0b2f7Stbbdev     CacheBinType cacheBinModel;
90551c0b2f7Stbbdev     // The reference to real cache bin inside the large object cache.
90651c0b2f7Stbbdev     CacheBinType &cacheBin;
90751c0b2f7Stbbdev 
90851c0b2f7Stbbdev     const size_t size;
90951c0b2f7Stbbdev 
91051c0b2f7Stbbdev     // save only current time
91151c0b2f7Stbbdev     std::list<uintptr_t> objects;
91251c0b2f7Stbbdev 
91351c0b2f7Stbbdev     void doCleanup() {
91451c0b2f7Stbbdev         if ( cacheBinModel.cachedSize > Props::TooLargeFactor*cacheBinModel.usedSize ) tooLargeLOC++;
91551c0b2f7Stbbdev         else tooLargeLOC = 0;
91651c0b2f7Stbbdev 
91751c0b2f7Stbbdev         if (tooLargeLOC>3 && cacheBinModel.ageThreshold)
91851c0b2f7Stbbdev             cacheBinModel.ageThreshold = (cacheBinModel.ageThreshold + cacheBinModel.meanHitRange)/2;
91951c0b2f7Stbbdev 
92051c0b2f7Stbbdev         uintptr_t currTime = cacheCurrTime;
92151c0b2f7Stbbdev         while (!objects.empty() && (intptr_t)(currTime - objects.front()) > cacheBinModel.ageThreshold) {
92251c0b2f7Stbbdev             cacheBinModel.cachedSize -= size;
92351c0b2f7Stbbdev             cacheBinModel.lastCleanedAge = objects.front();
92451c0b2f7Stbbdev             objects.pop_front();
92551c0b2f7Stbbdev         }
92651c0b2f7Stbbdev 
92751c0b2f7Stbbdev         cacheBinModel.oldest = objects.empty() ? 0 : objects.front();
92851c0b2f7Stbbdev     }
92951c0b2f7Stbbdev 
93051c0b2f7Stbbdev public:
93151c0b2f7Stbbdev     CacheBinModel(CacheBinType &_cacheBin, size_t allocSize) : cacheBin(_cacheBin), size(allocSize) {
93251c0b2f7Stbbdev         cacheBinModel.oldest = cacheBin.oldest;
93351c0b2f7Stbbdev         cacheBinModel.lastCleanedAge = cacheBin.lastCleanedAge;
93451c0b2f7Stbbdev         cacheBinModel.ageThreshold = cacheBin.ageThreshold;
93551c0b2f7Stbbdev         cacheBinModel.usedSize = cacheBin.usedSize;
93651c0b2f7Stbbdev         cacheBinModel.cachedSize = cacheBin.cachedSize;
93751c0b2f7Stbbdev         cacheBinModel.meanHitRange = cacheBin.meanHitRange;
93851c0b2f7Stbbdev         cacheBinModel.lastGet = cacheBin.lastGet;
93951c0b2f7Stbbdev     }
94051c0b2f7Stbbdev     void get() {
94151c0b2f7Stbbdev         uintptr_t currTime = ++cacheCurrTime;
94251c0b2f7Stbbdev 
94351c0b2f7Stbbdev         if ( objects.empty() ) {
94451c0b2f7Stbbdev             const uintptr_t sinceLastGet = currTime - cacheBinModel.lastGet;
94551c0b2f7Stbbdev             if ( ( cacheBinModel.ageThreshold && sinceLastGet > Props::LongWaitFactor*cacheBinModel.ageThreshold ) ||
94651c0b2f7Stbbdev                  ( cacheBinModel.lastCleanedAge && sinceLastGet > Props::LongWaitFactor*(cacheBinModel.lastCleanedAge - cacheBinModel.lastGet) ) )
94751c0b2f7Stbbdev                 cacheBinModel.lastCleanedAge = cacheBinModel.ageThreshold = 0;
94851c0b2f7Stbbdev 
94951c0b2f7Stbbdev             if (cacheBinModel.lastCleanedAge)
95051c0b2f7Stbbdev                 cacheBinModel.ageThreshold = Props::OnMissFactor*(currTime - cacheBinModel.lastCleanedAge);
95151c0b2f7Stbbdev         } else {
95251c0b2f7Stbbdev             uintptr_t obj_age = objects.back();
95351c0b2f7Stbbdev             objects.pop_back();
95451c0b2f7Stbbdev             if ( objects.empty() ) cacheBinModel.oldest = 0;
95551c0b2f7Stbbdev 
95651c0b2f7Stbbdev             intptr_t hitRange = currTime - obj_age;
95751c0b2f7Stbbdev             cacheBinModel.meanHitRange = cacheBinModel.meanHitRange? (cacheBinModel.meanHitRange + hitRange)/2 : hitRange;
95851c0b2f7Stbbdev 
95951c0b2f7Stbbdev             cacheBinModel.cachedSize -= size;
96051c0b2f7Stbbdev         }
96151c0b2f7Stbbdev 
96251c0b2f7Stbbdev         cacheBinModel.usedSize += size;
96351c0b2f7Stbbdev         cacheBinModel.lastGet = currTime;
96451c0b2f7Stbbdev 
96551c0b2f7Stbbdev         if ( currTime % rml::internal::cacheCleanupFreq == 0 ) doCleanup();
96651c0b2f7Stbbdev     }
96751c0b2f7Stbbdev 
96851c0b2f7Stbbdev     void putList( int num ) {
96951c0b2f7Stbbdev         uintptr_t currTime = cacheCurrTime;
97051c0b2f7Stbbdev         cacheCurrTime += num;
97151c0b2f7Stbbdev 
97251c0b2f7Stbbdev         cacheBinModel.usedSize -= num*size;
97351c0b2f7Stbbdev 
97451c0b2f7Stbbdev         bool cleanUpNeeded = false;
97551c0b2f7Stbbdev         if ( !cacheBinModel.lastCleanedAge ) {
97651c0b2f7Stbbdev             cacheBinModel.lastCleanedAge = ++currTime;
97751c0b2f7Stbbdev             cleanUpNeeded |= currTime % rml::internal::cacheCleanupFreq == 0;
97851c0b2f7Stbbdev             num--;
97951c0b2f7Stbbdev         }
98051c0b2f7Stbbdev 
98151c0b2f7Stbbdev         for ( int i=1; i<=num; ++i ) {
98251c0b2f7Stbbdev             currTime+=1;
98351c0b2f7Stbbdev             cleanUpNeeded |= currTime % rml::internal::cacheCleanupFreq == 0;
98451c0b2f7Stbbdev             if ( objects.empty() )
98551c0b2f7Stbbdev                 cacheBinModel.oldest = currTime;
98651c0b2f7Stbbdev             objects.push_back(currTime);
98751c0b2f7Stbbdev         }
98851c0b2f7Stbbdev 
98951c0b2f7Stbbdev         cacheBinModel.cachedSize += num*size;
99051c0b2f7Stbbdev 
99151c0b2f7Stbbdev         if ( cleanUpNeeded ) doCleanup();
99251c0b2f7Stbbdev     }
99351c0b2f7Stbbdev 
99451c0b2f7Stbbdev     void check() {
99551c0b2f7Stbbdev         REQUIRE(cacheBinModel.oldest == cacheBin.oldest);
99651c0b2f7Stbbdev         REQUIRE(cacheBinModel.lastCleanedAge == cacheBin.lastCleanedAge);
99751c0b2f7Stbbdev         REQUIRE(cacheBinModel.ageThreshold == cacheBin.ageThreshold);
99851c0b2f7Stbbdev         REQUIRE(cacheBinModel.usedSize == cacheBin.usedSize);
99951c0b2f7Stbbdev         REQUIRE(cacheBinModel.cachedSize == cacheBin.cachedSize);
100051c0b2f7Stbbdev         REQUIRE(cacheBinModel.meanHitRange == cacheBin.meanHitRange);
100151c0b2f7Stbbdev         REQUIRE(cacheBinModel.lastGet == cacheBin.lastGet);
100251c0b2f7Stbbdev     }
100351c0b2f7Stbbdev 
100451c0b2f7Stbbdev     static uintptr_t cacheCurrTime;
100551c0b2f7Stbbdev     static intptr_t tooLargeLOC;
100651c0b2f7Stbbdev };
100751c0b2f7Stbbdev 
100851c0b2f7Stbbdev template<typename Props> uintptr_t CacheBinModel<Props>::cacheCurrTime;
100951c0b2f7Stbbdev template<typename Props> intptr_t CacheBinModel<Props>::tooLargeLOC;
101051c0b2f7Stbbdev 
101151c0b2f7Stbbdev template <typename Scenario>
101251c0b2f7Stbbdev void LOCModelTester() {
101351c0b2f7Stbbdev     defaultMemPool->extMemPool.loc.cleanAll();
101451c0b2f7Stbbdev     defaultMemPool->extMemPool.loc.reset();
101551c0b2f7Stbbdev 
101651c0b2f7Stbbdev     const size_t size = 16 * 1024;
101751c0b2f7Stbbdev     const size_t headersSize = sizeof(rml::internal::LargeMemoryBlock)+sizeof(rml::internal::LargeObjectHdr);
101851c0b2f7Stbbdev     const size_t allocationSize = LargeObjectCache::alignToBin(size+headersSize+rml::internal::largeObjectAlignment);
101951c0b2f7Stbbdev     const int binIdx = defaultMemPool->extMemPool.loc.largeCache.sizeToIdx( allocationSize );
102051c0b2f7Stbbdev 
102151c0b2f7Stbbdev     CacheBinModel<rml::internal::LargeObjectCache::LargeCacheTypeProps>::cacheCurrTime = defaultMemPool->extMemPool.loc.cacheCurrTime;
102251c0b2f7Stbbdev     CacheBinModel<rml::internal::LargeObjectCache::LargeCacheTypeProps>::tooLargeLOC = defaultMemPool->extMemPool.loc.largeCache.tooLargeLOC;
102351c0b2f7Stbbdev     CacheBinModel<rml::internal::LargeObjectCache::LargeCacheTypeProps> cacheBinModel(defaultMemPool->extMemPool.loc.largeCache.bin[binIdx], allocationSize);
102451c0b2f7Stbbdev 
102551c0b2f7Stbbdev     Scenario scen;
102651c0b2f7Stbbdev     for (rml::internal::LargeMemoryBlock *lmb = scen.next(); (intptr_t)lmb != (intptr_t)-1; lmb = scen.next()) {
102751c0b2f7Stbbdev         if ( lmb ) {
102851c0b2f7Stbbdev             int num=1;
102951c0b2f7Stbbdev             for (rml::internal::LargeMemoryBlock *curr = lmb; curr->next; curr=curr->next) num+=1;
103051c0b2f7Stbbdev             defaultMemPool->extMemPool.freeLargeObject(lmb);
103151c0b2f7Stbbdev             cacheBinModel.putList(num);
103251c0b2f7Stbbdev         } else {
103351c0b2f7Stbbdev             scen.saveLmb(defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize));
103451c0b2f7Stbbdev             cacheBinModel.get();
103551c0b2f7Stbbdev         }
103651c0b2f7Stbbdev 
103751c0b2f7Stbbdev         cacheBinModel.check();
103851c0b2f7Stbbdev     }
103951c0b2f7Stbbdev }
104051c0b2f7Stbbdev 
104151c0b2f7Stbbdev class TestBootstrap {
104251c0b2f7Stbbdev     bool allocating;
104351c0b2f7Stbbdev     std::vector<rml::internal::LargeMemoryBlock*> lmbArray;
104451c0b2f7Stbbdev public:
104551c0b2f7Stbbdev     TestBootstrap() : allocating(true) {}
104651c0b2f7Stbbdev 
104751c0b2f7Stbbdev     rml::internal::LargeMemoryBlock* next() {
104851c0b2f7Stbbdev         if ( allocating )
104951c0b2f7Stbbdev             return NULL;
105051c0b2f7Stbbdev         if ( !lmbArray.empty() ) {
105151c0b2f7Stbbdev             rml::internal::LargeMemoryBlock *ret = lmbArray.back();
105251c0b2f7Stbbdev             lmbArray.pop_back();
105351c0b2f7Stbbdev             return ret;
105451c0b2f7Stbbdev         }
105551c0b2f7Stbbdev         return (rml::internal::LargeMemoryBlock*)-1;
105651c0b2f7Stbbdev     }
105751c0b2f7Stbbdev 
105851c0b2f7Stbbdev     void saveLmb( rml::internal::LargeMemoryBlock *lmb ) {
105951c0b2f7Stbbdev         lmb->next = NULL;
106051c0b2f7Stbbdev         lmbArray.push_back(lmb);
106151c0b2f7Stbbdev         if ( lmbArray.size() == 1000 ) allocating = false;
106251c0b2f7Stbbdev     }
106351c0b2f7Stbbdev };
106451c0b2f7Stbbdev 
106551c0b2f7Stbbdev class TestRandom {
106651c0b2f7Stbbdev     std::vector<rml::internal::LargeMemoryBlock*> lmbArray;
106751c0b2f7Stbbdev     int numOps;
106851c0b2f7Stbbdev public:
106951c0b2f7Stbbdev     TestRandom() : numOps(100000) {
107051c0b2f7Stbbdev         srand(1234);
107151c0b2f7Stbbdev     }
107251c0b2f7Stbbdev 
107351c0b2f7Stbbdev     rml::internal::LargeMemoryBlock* next() {
107451c0b2f7Stbbdev         if ( numOps-- ) {
107551c0b2f7Stbbdev             if ( lmbArray.empty() || rand() / (RAND_MAX>>1) == 0 )
107651c0b2f7Stbbdev                 return NULL;
107751c0b2f7Stbbdev             size_t ind = rand()%lmbArray.size();
107851c0b2f7Stbbdev             if ( ind != lmbArray.size()-1 ) std::swap(lmbArray[ind],lmbArray[lmbArray.size()-1]);
107951c0b2f7Stbbdev             rml::internal::LargeMemoryBlock *lmb = lmbArray.back();
108051c0b2f7Stbbdev             lmbArray.pop_back();
108151c0b2f7Stbbdev             return lmb;
108251c0b2f7Stbbdev         }
108351c0b2f7Stbbdev         return (rml::internal::LargeMemoryBlock*)-1;
108451c0b2f7Stbbdev     }
108551c0b2f7Stbbdev 
108651c0b2f7Stbbdev     void saveLmb( rml::internal::LargeMemoryBlock *lmb ) {
108751c0b2f7Stbbdev         lmb->next = NULL;
108851c0b2f7Stbbdev         lmbArray.push_back(lmb);
108951c0b2f7Stbbdev     }
109051c0b2f7Stbbdev };
109151c0b2f7Stbbdev 
109251c0b2f7Stbbdev class TestCollapsingMallocFree : public SimpleBarrier {
109351c0b2f7Stbbdev public:
109451c0b2f7Stbbdev     static const int NUM_ALLOCS = 100000;
109551c0b2f7Stbbdev     const int num_threads;
109651c0b2f7Stbbdev 
109751c0b2f7Stbbdev     TestCollapsingMallocFree( int _num_threads ) : num_threads(_num_threads) {
109851c0b2f7Stbbdev         initBarrier( num_threads );
109951c0b2f7Stbbdev     }
110051c0b2f7Stbbdev 
110151c0b2f7Stbbdev     void operator() ( int ) const {
110251c0b2f7Stbbdev         const size_t size = 16 * 1024;
110351c0b2f7Stbbdev         const size_t headersSize = sizeof(rml::internal::LargeMemoryBlock)+sizeof(rml::internal::LargeObjectHdr);
110451c0b2f7Stbbdev         const size_t allocationSize = LargeObjectCache::alignToBin(size+headersSize+rml::internal::largeObjectAlignment);
110551c0b2f7Stbbdev 
110651c0b2f7Stbbdev         barrier.wait();
110751c0b2f7Stbbdev         for ( int i=0; i<NUM_ALLOCS; ++i ) {
110851c0b2f7Stbbdev             defaultMemPool->extMemPool.freeLargeObject(
110951c0b2f7Stbbdev                 defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize) );
111051c0b2f7Stbbdev         }
111151c0b2f7Stbbdev     }
111251c0b2f7Stbbdev 
111351c0b2f7Stbbdev     void check() {
111451c0b2f7Stbbdev         REQUIRE( tbbmalloc_whitebox::locGetProcessed == tbbmalloc_whitebox::locPutProcessed);
111551c0b2f7Stbbdev         REQUIRE_MESSAGE( tbbmalloc_whitebox::locGetProcessed < num_threads*NUM_ALLOCS, "No one Malloc/Free pair was collapsed." );
111651c0b2f7Stbbdev     }
111751c0b2f7Stbbdev };
111851c0b2f7Stbbdev 
111951c0b2f7Stbbdev class TestCollapsingBootstrap : public SimpleBarrier {
112051c0b2f7Stbbdev     class CheckNumAllocs {
112151c0b2f7Stbbdev         const int num_threads;
112251c0b2f7Stbbdev     public:
112351c0b2f7Stbbdev         CheckNumAllocs( int _num_threads ) : num_threads(_num_threads) {}
112451c0b2f7Stbbdev         void operator()() const {
112551c0b2f7Stbbdev             REQUIRE( tbbmalloc_whitebox::locGetProcessed == num_threads*NUM_ALLOCS );
112651c0b2f7Stbbdev             REQUIRE( tbbmalloc_whitebox::locPutProcessed == 0 );
112751c0b2f7Stbbdev         }
112851c0b2f7Stbbdev     };
112951c0b2f7Stbbdev public:
113051c0b2f7Stbbdev     static const int NUM_ALLOCS = 1000;
113151c0b2f7Stbbdev     const int num_threads;
113251c0b2f7Stbbdev 
113351c0b2f7Stbbdev     TestCollapsingBootstrap( int _num_threads ) : num_threads(_num_threads) {
113451c0b2f7Stbbdev         initBarrier( num_threads );
113551c0b2f7Stbbdev     }
113651c0b2f7Stbbdev 
113751c0b2f7Stbbdev     void operator() ( int ) const {
113851c0b2f7Stbbdev         const size_t size = 16 * 1024;
113951c0b2f7Stbbdev         size_t headersSize = sizeof(rml::internal::LargeMemoryBlock)+sizeof(rml::internal::LargeObjectHdr);
114051c0b2f7Stbbdev         size_t allocationSize = LargeObjectCache::alignToBin(size+headersSize+rml::internal::largeObjectAlignment);
114151c0b2f7Stbbdev 
114251c0b2f7Stbbdev         barrier.wait();
114351c0b2f7Stbbdev         rml::internal::LargeMemoryBlock *lmbArray[NUM_ALLOCS];
114451c0b2f7Stbbdev         for ( int i=0; i<NUM_ALLOCS; ++i )
114551c0b2f7Stbbdev             lmbArray[i] = defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize);
114651c0b2f7Stbbdev 
114751c0b2f7Stbbdev         barrier.wait(CheckNumAllocs(num_threads));
114851c0b2f7Stbbdev         for ( int i=0; i<NUM_ALLOCS; ++i )
114951c0b2f7Stbbdev             defaultMemPool->extMemPool.freeLargeObject( lmbArray[i] );
115051c0b2f7Stbbdev     }
115151c0b2f7Stbbdev 
115251c0b2f7Stbbdev     void check() {
115351c0b2f7Stbbdev         REQUIRE( tbbmalloc_whitebox::locGetProcessed == tbbmalloc_whitebox::locPutProcessed );
115451c0b2f7Stbbdev         REQUIRE( tbbmalloc_whitebox::locGetProcessed == num_threads*NUM_ALLOCS );
115551c0b2f7Stbbdev     }
115651c0b2f7Stbbdev };
115751c0b2f7Stbbdev 
115851c0b2f7Stbbdev template <typename Scenario>
115951c0b2f7Stbbdev void LOCCollapsingTester( int num_threads ) {
116051c0b2f7Stbbdev     tbbmalloc_whitebox::locGetProcessed = 0;
116151c0b2f7Stbbdev     tbbmalloc_whitebox::locPutProcessed = 0;
116251c0b2f7Stbbdev     defaultMemPool->extMemPool.loc.cleanAll();
116351c0b2f7Stbbdev     defaultMemPool->extMemPool.loc.reset();
116451c0b2f7Stbbdev 
116551c0b2f7Stbbdev     Scenario scen(num_threads);
116651c0b2f7Stbbdev     utils::NativeParallelFor(num_threads, scen);
116751c0b2f7Stbbdev 
116851c0b2f7Stbbdev     scen.check();
116951c0b2f7Stbbdev }
117051c0b2f7Stbbdev 
117151c0b2f7Stbbdev void TestLOC() {
117251c0b2f7Stbbdev     LOCModelTester<TestBootstrap>();
117351c0b2f7Stbbdev     LOCModelTester<TestRandom>();
117451c0b2f7Stbbdev 
117551c0b2f7Stbbdev     const int num_threads = 16;
117651c0b2f7Stbbdev     LOCCollapsingTester<TestCollapsingBootstrap>( num_threads );
117751c0b2f7Stbbdev     if ( num_threads > 1 ) {
117851c0b2f7Stbbdev         INFO( "num_threads = " << num_threads );
117951c0b2f7Stbbdev         LOCCollapsingTester<TestCollapsingMallocFree>( num_threads );
118051c0b2f7Stbbdev     } else {
118151c0b2f7Stbbdev         REPORT( "Warning: concurrency is too low for TestMallocFreeCollapsing ( num_threads = %d )\n", num_threads );
118251c0b2f7Stbbdev     }
118351c0b2f7Stbbdev }
118451c0b2f7Stbbdev /*---------------------------------------------------------------------------*/
118551c0b2f7Stbbdev 
118651c0b2f7Stbbdev void *findCacheLine(void *p) {
118751c0b2f7Stbbdev     return (void*)alignDown((uintptr_t)p, estimatedCacheLineSize);
118851c0b2f7Stbbdev }
118951c0b2f7Stbbdev 
119051c0b2f7Stbbdev // test that internals of Block are at expected cache lines
119151c0b2f7Stbbdev void TestSlabAlignment() {
119251c0b2f7Stbbdev     const size_t min_sz = 8;
119351c0b2f7Stbbdev     const int space = 2*16*1024; // fill at least 2 slabs
119451c0b2f7Stbbdev     void *pointers[space / min_sz];  // the worst case is min_sz byte object
119551c0b2f7Stbbdev 
119651c0b2f7Stbbdev     for (size_t sz = min_sz; sz <= 64; sz *= 2) {
119751c0b2f7Stbbdev         for (size_t i = 0; i < space/sz; i++) {
119851c0b2f7Stbbdev             pointers[i] = scalable_malloc(sz);
119951c0b2f7Stbbdev             Block *block = (Block *)alignDown(pointers[i], slabSize);
120051c0b2f7Stbbdev             REQUIRE_MESSAGE(findCacheLine(&block->isFull) != findCacheLine(pointers[i]),
120151c0b2f7Stbbdev                           "A user object must not share a cache line with slab control structures.");
120251c0b2f7Stbbdev             REQUIRE_MESSAGE(findCacheLine(&block->next) != findCacheLine(&block->nextPrivatizable),
120351c0b2f7Stbbdev                           "GlobalBlockFields and LocalBlockFields must be on different cache lines.");
120451c0b2f7Stbbdev         }
120551c0b2f7Stbbdev         for (size_t i = 0; i < space/sz; i++)
120651c0b2f7Stbbdev             scalable_free(pointers[i]);
120751c0b2f7Stbbdev     }
120851c0b2f7Stbbdev }
120951c0b2f7Stbbdev 
121051c0b2f7Stbbdev #include "common/memory_usage.h"
121151c0b2f7Stbbdev 
121251c0b2f7Stbbdev // TODO: Consider adding Huge Pages support on macOS (special mmap flag).
121351c0b2f7Stbbdev // Transparent Huge pages support could be enabled by different system parsing mechanism,
121451c0b2f7Stbbdev // because there is no /proc/meminfo on macOS
121551c0b2f7Stbbdev #if __linux__
121651c0b2f7Stbbdev void TestTHP() {
121751c0b2f7Stbbdev     // Get backend from default memory pool
121851c0b2f7Stbbdev     rml::internal::Backend *backend = &(defaultMemPool->extMemPool.backend);
121951c0b2f7Stbbdev 
122051c0b2f7Stbbdev     // Configure malloc to use huge pages
122151c0b2f7Stbbdev     scalable_allocation_mode(USE_HUGE_PAGES, 1);
122251c0b2f7Stbbdev     REQUIRE_MESSAGE(hugePages.isEnabled, "Huge pages should be enabled via scalable_allocation_mode");
122351c0b2f7Stbbdev 
122451c0b2f7Stbbdev     const int HUGE_PAGE_SIZE = 2 * 1024 * 1024;
122551c0b2f7Stbbdev 
122651c0b2f7Stbbdev     // allocCount transparent huge pages should be allocated
122751c0b2f7Stbbdev     const int allocCount = 10;
122851c0b2f7Stbbdev 
122951c0b2f7Stbbdev     // Allocate huge page aligned memory regions to track system
123051c0b2f7Stbbdev     // counters for transparent huge pages
123151c0b2f7Stbbdev     void*  allocPtrs[allocCount];
123251c0b2f7Stbbdev 
123351c0b2f7Stbbdev     // Wait for the system to update process memory info files after other tests
123451c0b2f7Stbbdev     utils::Sleep(4000);
123551c0b2f7Stbbdev 
123651c0b2f7Stbbdev     // Parse system info regarding current THP status
123751c0b2f7Stbbdev     size_t currentSystemTHPCount = utils::getSystemTHPCount();
123851c0b2f7Stbbdev     size_t currentSystemTHPAllocatedSize = utils::getSystemTHPAllocatedSize();
123951c0b2f7Stbbdev 
124051c0b2f7Stbbdev     for (int i = 0; i < allocCount; i++) {
124151c0b2f7Stbbdev         // Allocation size have to be aligned on page size
124251c0b2f7Stbbdev         size_t allocSize = HUGE_PAGE_SIZE - (i * 1000);
124351c0b2f7Stbbdev 
124451c0b2f7Stbbdev         // Map memory
124551c0b2f7Stbbdev         allocPtrs[i] = backend->allocRawMem(allocSize);
124651c0b2f7Stbbdev 
124751c0b2f7Stbbdev         REQUIRE_MESSAGE(allocPtrs[i], "Allocation not succeeded.");
124851c0b2f7Stbbdev         REQUIRE_MESSAGE(allocSize == HUGE_PAGE_SIZE,
124951c0b2f7Stbbdev             "Allocation size have to be aligned on Huge Page size internally.");
125051c0b2f7Stbbdev 
125151c0b2f7Stbbdev         // First touch policy - no real pages allocated by OS without accessing the region
125251c0b2f7Stbbdev         memset(allocPtrs[i], 1, allocSize);
125351c0b2f7Stbbdev 
125451c0b2f7Stbbdev         REQUIRE_MESSAGE(isAligned(allocPtrs[i], HUGE_PAGE_SIZE),
125551c0b2f7Stbbdev             "The pointer returned by scalable_malloc is not aligned on huge page size.");
125651c0b2f7Stbbdev     }
125751c0b2f7Stbbdev 
125851c0b2f7Stbbdev     // Wait for the system to update process memory info files after allocations
125951c0b2f7Stbbdev     utils::Sleep(4000);
126051c0b2f7Stbbdev 
126151c0b2f7Stbbdev     // Generally, kernel tries to allocate transparent huge pages, but sometimes it cannot do this
126251c0b2f7Stbbdev     // (tested on SLES 11/12), so consider this system info checks as a remark.
126351c0b2f7Stbbdev     // Also, some systems can allocate more memory then needed in background (tested on Ubuntu 14.04)
126451c0b2f7Stbbdev     size_t newSystemTHPCount = utils::getSystemTHPCount();
126551c0b2f7Stbbdev     size_t newSystemTHPAllocatedSize = utils::getSystemTHPAllocatedSize();
126651c0b2f7Stbbdev     if ((newSystemTHPCount - currentSystemTHPCount) < allocCount
126751c0b2f7Stbbdev             && (newSystemTHPAllocatedSize - currentSystemTHPAllocatedSize) / (2 * 1024) < allocCount) {
126851c0b2f7Stbbdev         REPORT( "Warning: the system didn't allocate needed amount of THPs.\n" );
126951c0b2f7Stbbdev     }
127051c0b2f7Stbbdev 
127151c0b2f7Stbbdev     // Test memory unmap
127251c0b2f7Stbbdev     for (int i = 0; i < allocCount; i++) {
127351c0b2f7Stbbdev         REQUIRE_MESSAGE(backend->freeRawMem(allocPtrs[i], HUGE_PAGE_SIZE),
127451c0b2f7Stbbdev                 "Something went wrong during raw memory free");
127551c0b2f7Stbbdev     }
127651c0b2f7Stbbdev }
127751c0b2f7Stbbdev #endif // __linux__
127851c0b2f7Stbbdev 
127951c0b2f7Stbbdev inline size_t getStabilizedMemUsage() {
128051c0b2f7Stbbdev     for (int i = 0; i < 3; i++) utils::GetMemoryUsage();
128151c0b2f7Stbbdev     return utils::GetMemoryUsage();
128251c0b2f7Stbbdev }
128351c0b2f7Stbbdev 
128451c0b2f7Stbbdev inline void* reallocAndRetrieve(void* origPtr, size_t reallocSize, size_t& origBlockSize, size_t& reallocBlockSize) {
128551c0b2f7Stbbdev     rml::internal::LargeMemoryBlock* origLmb = ((rml::internal::LargeObjectHdr *)origPtr - 1)->memoryBlock;
128651c0b2f7Stbbdev     origBlockSize = origLmb->unalignedSize;
128751c0b2f7Stbbdev 
128851c0b2f7Stbbdev     void* reallocPtr = rml::internal::reallocAligned(defaultMemPool, origPtr, reallocSize, 0);
128951c0b2f7Stbbdev 
129051c0b2f7Stbbdev     // Retrieved reallocated block information
129151c0b2f7Stbbdev     rml::internal::LargeMemoryBlock* reallocLmb = ((rml::internal::LargeObjectHdr *)reallocPtr - 1)->memoryBlock;
129251c0b2f7Stbbdev     reallocBlockSize = reallocLmb->unalignedSize;
129351c0b2f7Stbbdev 
129451c0b2f7Stbbdev     return reallocPtr;
129551c0b2f7Stbbdev }
129651c0b2f7Stbbdev 
129751c0b2f7Stbbdev void TestReallocDecreasing() {
129851c0b2f7Stbbdev 
129951c0b2f7Stbbdev     /* Testing that actual reallocation happens for large objects that do not fit the backend cache
130051c0b2f7Stbbdev        but decrease in size by a factor of >= 2. */
130151c0b2f7Stbbdev 
130251c0b2f7Stbbdev     size_t startSize = 100 * 1024 * 1024;
130351c0b2f7Stbbdev     size_t maxBinnedSize = defaultMemPool->extMemPool.backend.getMaxBinnedSize();
130451c0b2f7Stbbdev     void*  origPtr = scalable_malloc(startSize);
130551c0b2f7Stbbdev     void*  reallocPtr = NULL;
130651c0b2f7Stbbdev 
130751c0b2f7Stbbdev     // Realloc on 1MB less size
130851c0b2f7Stbbdev     size_t origBlockSize = 42;
130951c0b2f7Stbbdev     size_t reallocBlockSize = 43;
131051c0b2f7Stbbdev     reallocPtr = reallocAndRetrieve(origPtr, startSize - 1 * 1024 * 1024, origBlockSize, reallocBlockSize);
131151c0b2f7Stbbdev     REQUIRE_MESSAGE(origBlockSize == reallocBlockSize, "Reallocated block size shouldn't change");
131251c0b2f7Stbbdev     REQUIRE_MESSAGE(reallocPtr == origPtr, "Original pointer shouldn't change");
131351c0b2f7Stbbdev 
131451c0b2f7Stbbdev     // Repeated decreasing reallocation while max cache bin size reached
131551c0b2f7Stbbdev     size_t reallocSize = (startSize / 2) - 1000; // exact realloc
131651c0b2f7Stbbdev     while(reallocSize > maxBinnedSize) {
131751c0b2f7Stbbdev 
131851c0b2f7Stbbdev         // Prevent huge/large objects caching
131951c0b2f7Stbbdev         defaultMemPool->extMemPool.loc.cleanAll();
132051c0b2f7Stbbdev         // Prevent local large object caching
132151c0b2f7Stbbdev         TLSData *tls = defaultMemPool->getTLS(/*create=*/false);
132251c0b2f7Stbbdev         tls->lloc.externalCleanup(&defaultMemPool->extMemPool);
132351c0b2f7Stbbdev 
132451c0b2f7Stbbdev         size_t sysMemUsageBefore = getStabilizedMemUsage();
132551c0b2f7Stbbdev         size_t totalMemSizeBefore = defaultMemPool->extMemPool.backend.getTotalMemSize();
132651c0b2f7Stbbdev 
132751c0b2f7Stbbdev         reallocPtr = reallocAndRetrieve(origPtr, reallocSize, origBlockSize, reallocBlockSize);
132851c0b2f7Stbbdev 
132951c0b2f7Stbbdev         REQUIRE_MESSAGE(origBlockSize > reallocBlockSize, "Reallocated block size should descrease.");
133051c0b2f7Stbbdev 
133151c0b2f7Stbbdev         size_t sysMemUsageAfter = getStabilizedMemUsage();
133251c0b2f7Stbbdev         size_t totalMemSizeAfter = defaultMemPool->extMemPool.backend.getTotalMemSize();
133351c0b2f7Stbbdev 
133451c0b2f7Stbbdev         // Prevent false checking when backend caching occurred or could not read system memory usage info
133551c0b2f7Stbbdev         if (totalMemSizeBefore > totalMemSizeAfter && sysMemUsageAfter != 0 && sysMemUsageBefore != 0) {
133651c0b2f7Stbbdev             REQUIRE_MESSAGE(sysMemUsageBefore > sysMemUsageAfter, "Memory were not released");
133751c0b2f7Stbbdev         }
133851c0b2f7Stbbdev 
133951c0b2f7Stbbdev         origPtr = reallocPtr;
134051c0b2f7Stbbdev         reallocSize = (reallocSize / 2) - 1000; // exact realloc
134151c0b2f7Stbbdev     }
134251c0b2f7Stbbdev     scalable_free(reallocPtr);
134351c0b2f7Stbbdev 
134451c0b2f7Stbbdev     /* TODO: Decreasing reallocation of large objects that fit backend cache */
134551c0b2f7Stbbdev     /* TODO: Small objects decreasing reallocation test */
134651c0b2f7Stbbdev }
134751c0b2f7Stbbdev #if !__TBB_WIN8UI_SUPPORT && defined(_WIN32)
134851c0b2f7Stbbdev 
134951c0b2f7Stbbdev #include "../../src/tbbmalloc_proxy/function_replacement.cpp"
135051c0b2f7Stbbdev #include <string>
135151c0b2f7Stbbdev namespace FunctionReplacement {
135251c0b2f7Stbbdev     FunctionInfo funcInfo = { "funcname","dllname" };
135351c0b2f7Stbbdev     char **func_replacement_log;
135451c0b2f7Stbbdev     int status;
135551c0b2f7Stbbdev 
135651c0b2f7Stbbdev     void LogCleanup() {
135751c0b2f7Stbbdev         // Free all allocated memory
135851c0b2f7Stbbdev         for (unsigned i = 0; i < Log::record_number; i++){
135951c0b2f7Stbbdev             HeapFree(GetProcessHeap(), 0, Log::records[i]);
136051c0b2f7Stbbdev         }
136151c0b2f7Stbbdev         for (unsigned i = 0; i < Log::RECORDS_COUNT + 1; i++){
136251c0b2f7Stbbdev             Log::records[i] = NULL;
136351c0b2f7Stbbdev         }
136451c0b2f7Stbbdev         Log::replacement_status = true;
136551c0b2f7Stbbdev         Log::record_number = 0;
136651c0b2f7Stbbdev     }
136751c0b2f7Stbbdev 
136851c0b2f7Stbbdev     void TestEmptyLog() {
136951c0b2f7Stbbdev         status = TBB_malloc_replacement_log(&func_replacement_log);
137051c0b2f7Stbbdev 
137151c0b2f7Stbbdev         REQUIRE_MESSAGE(status == -1, "Status is true, but log is empty");
137251c0b2f7Stbbdev         REQUIRE_MESSAGE(*func_replacement_log == nullptr, "Log must be empty");
137351c0b2f7Stbbdev     }
137451c0b2f7Stbbdev 
137551c0b2f7Stbbdev     void TestLogOverload() {
137651c0b2f7Stbbdev         for (int i = 0; i < 1000; i++)
137751c0b2f7Stbbdev             Log::record(funcInfo, "opcode string", true);
137851c0b2f7Stbbdev 
137951c0b2f7Stbbdev         status = TBB_malloc_replacement_log(&func_replacement_log);
138051c0b2f7Stbbdev         // Find last record
138151c0b2f7Stbbdev         for (; *(func_replacement_log + 1) != 0; func_replacement_log++) {}
138251c0b2f7Stbbdev 
138351c0b2f7Stbbdev         std::string last_line(*func_replacement_log);
138451c0b2f7Stbbdev         REQUIRE_MESSAGE(status == 0, "False status, but all functions found");
138551c0b2f7Stbbdev         REQUIRE_MESSAGE(last_line.compare("Log was truncated.") == 0, "Log overflow was not handled");
138651c0b2f7Stbbdev 
138751c0b2f7Stbbdev         // Change status
138851c0b2f7Stbbdev         Log::record(funcInfo, "opcode string", false);
138951c0b2f7Stbbdev         status = TBB_malloc_replacement_log(NULL);
139051c0b2f7Stbbdev         REQUIRE_MESSAGE(status == -1, "Status is true, but we have false search case");
139151c0b2f7Stbbdev 
139251c0b2f7Stbbdev         LogCleanup();
139351c0b2f7Stbbdev     }
139451c0b2f7Stbbdev 
139551c0b2f7Stbbdev     void TestFalseSearchCase() {
139651c0b2f7Stbbdev         Log::record(funcInfo, "opcode string", false);
139751c0b2f7Stbbdev         std::string expected_line = "Fail: "+ std::string(funcInfo.funcName) + " (" +
139851c0b2f7Stbbdev                          std::string(funcInfo.dllName) + "), byte pattern: <opcode string>";
139951c0b2f7Stbbdev 
140051c0b2f7Stbbdev         status = TBB_malloc_replacement_log(&func_replacement_log);
140151c0b2f7Stbbdev 
140251c0b2f7Stbbdev         REQUIRE_MESSAGE(expected_line.compare(*func_replacement_log) == 0, "Wrong last string contnent");
140351c0b2f7Stbbdev         REQUIRE_MESSAGE(status == -1, "Status is true, but we have false search case");
140451c0b2f7Stbbdev         LogCleanup();
140551c0b2f7Stbbdev     }
140651c0b2f7Stbbdev 
140751c0b2f7Stbbdev     void TestWrongFunctionInDll(){
140851c0b2f7Stbbdev         HMODULE ucrtbase_handle = GetModuleHandle("ucrtbase.dll");
140951c0b2f7Stbbdev         if (ucrtbase_handle) {
141051c0b2f7Stbbdev             IsPrologueKnown("ucrtbase.dll", "fake_function", NULL, ucrtbase_handle);
141151c0b2f7Stbbdev             std::string expected_line = "Fail: fake_function (ucrtbase.dll), byte pattern: <unknown>";
141251c0b2f7Stbbdev 
141351c0b2f7Stbbdev             status = TBB_malloc_replacement_log(&func_replacement_log);
141451c0b2f7Stbbdev 
141551c0b2f7Stbbdev             REQUIRE_MESSAGE(expected_line.compare(*func_replacement_log) == 0, "Wrong last string contnent");
141651c0b2f7Stbbdev             REQUIRE_MESSAGE(status == -1, "Status is true, but we have false search case");
141751c0b2f7Stbbdev             LogCleanup();
141851c0b2f7Stbbdev         } else {
141951c0b2f7Stbbdev             INFO("Cannot found ucrtbase.dll on system, test skipped!\n");
142051c0b2f7Stbbdev         }
142151c0b2f7Stbbdev     }
142251c0b2f7Stbbdev }
142351c0b2f7Stbbdev 
142451c0b2f7Stbbdev void TesFunctionReplacementLog() {
142551c0b2f7Stbbdev     using namespace FunctionReplacement;
142651c0b2f7Stbbdev     // Do not reorder the test cases
142751c0b2f7Stbbdev     TestEmptyLog();
142851c0b2f7Stbbdev     TestLogOverload();
142951c0b2f7Stbbdev     TestFalseSearchCase();
143051c0b2f7Stbbdev     TestWrongFunctionInDll();
143151c0b2f7Stbbdev }
143251c0b2f7Stbbdev 
143351c0b2f7Stbbdev #endif /*!__TBB_WIN8UI_SUPPORT && defined(_WIN32)*/
143451c0b2f7Stbbdev 
143551c0b2f7Stbbdev #include <cmath> // pow function
143651c0b2f7Stbbdev 
143751c0b2f7Stbbdev // Huge objects cache: Size = MinSize * (2 ^ (Index / StepFactor) formula gives value for the bin size,
143851c0b2f7Stbbdev // but it is not matched with our sizeToIdx approximation algorithm, where step sizes between major
143951c0b2f7Stbbdev // (power of 2) sizes are equal. Used internally for the test. Static cast to avoid warnings.
144051c0b2f7Stbbdev inline size_t hocIdxToSizeFormula(int idx) {
144151c0b2f7Stbbdev     return static_cast<size_t>(float(rml::internal::LargeObjectCache::maxLargeSize) *
144251c0b2f7Stbbdev         pow(2, float(idx) / float(rml::internal::LargeObjectCache::HugeBSProps::StepFactor)));
144351c0b2f7Stbbdev }
144451c0b2f7Stbbdev // Large objects cache arithmetic progression
144551c0b2f7Stbbdev inline size_t locIdxToSizeFormula(int idx) {
144651c0b2f7Stbbdev     return rml::internal::LargeObjectCache::LargeBSProps::MinSize +
144751c0b2f7Stbbdev         (idx * rml::internal::LargeObjectCache::LargeBSProps::CacheStep);
144851c0b2f7Stbbdev }
144951c0b2f7Stbbdev 
145051c0b2f7Stbbdev template <typename CacheType>
145151c0b2f7Stbbdev void TestLOCacheBinsConverterImpl(int idx, size_t checkingSize) {
145251c0b2f7Stbbdev     size_t alignedSize = CacheType::alignToBin(checkingSize);
145351c0b2f7Stbbdev     REQUIRE_MESSAGE(alignedSize >= checkingSize, "Size is not correctly aligned");
145451c0b2f7Stbbdev     int calcIdx = CacheType::sizeToIdx(alignedSize);
145551c0b2f7Stbbdev     REQUIRE_MESSAGE(calcIdx == idx, "Index from size calculated not correctly");
145651c0b2f7Stbbdev }
145751c0b2f7Stbbdev 
145851c0b2f7Stbbdev void TestLOCacheBinsConverter(){
145951c0b2f7Stbbdev     typedef rml::internal::LargeObjectCache::LargeCacheType LargeCacheType;
146051c0b2f7Stbbdev     typedef rml::internal::LargeObjectCache::HugeCacheType HugeCacheType;
146151c0b2f7Stbbdev 
146251c0b2f7Stbbdev     size_t checkingSize = 0;
146351c0b2f7Stbbdev     for (int idx = 0; idx < LargeCacheType::numBins; idx++) {
146451c0b2f7Stbbdev         checkingSize = locIdxToSizeFormula(idx);
146551c0b2f7Stbbdev         TestLOCacheBinsConverterImpl<LargeCacheType>(idx, checkingSize);
146651c0b2f7Stbbdev     }
146751c0b2f7Stbbdev     for (int idx = 0; idx < HugeCacheType::numBins; idx++) {
146851c0b2f7Stbbdev         checkingSize = hocIdxToSizeFormula(idx);
146951c0b2f7Stbbdev         TestLOCacheBinsConverterImpl<HugeCacheType>(idx, checkingSize);
147051c0b2f7Stbbdev     }
147151c0b2f7Stbbdev }
147251c0b2f7Stbbdev 
147351c0b2f7Stbbdev struct HOThresholdTester {
147451c0b2f7Stbbdev     LargeObjectCache* loc;
147551c0b2f7Stbbdev     size_t hugeSize;
147651c0b2f7Stbbdev 
147751c0b2f7Stbbdev     static const size_t sieveSize = LargeObjectCache::defaultMaxHugeSize;
147851c0b2f7Stbbdev     // Sieve starts from 64MB (24-th cache bin), enough to check 4 bins radius range
147951c0b2f7Stbbdev     // for decent memory consumption (especially for 32-bit arch)
148051c0b2f7Stbbdev     static const int MIN_BIN_IDX = 21;
148151c0b2f7Stbbdev     static const int MAX_BIN_IDX = 27;
148251c0b2f7Stbbdev 
148351c0b2f7Stbbdev     enum CleanupType {
148451c0b2f7Stbbdev         NO_CLEANUP,
148551c0b2f7Stbbdev         REGULAR_CLEANUP,
148651c0b2f7Stbbdev         HARD_CLEANUP
148751c0b2f7Stbbdev     };
148851c0b2f7Stbbdev 
148951c0b2f7Stbbdev     void populateCache() {
149051c0b2f7Stbbdev         LargeMemoryBlock* loArray[MAX_BIN_IDX - MIN_BIN_IDX];
149151c0b2f7Stbbdev         // To avoid backend::softCacheCleanup consequences (cleanup by isLOCToolarge),
149251c0b2f7Stbbdev         // firstly allocate all objects and then cache them at once.
149351c0b2f7Stbbdev         // Morevover, just because first cache item will still be dropped from cache because of the lack of history,
149451c0b2f7Stbbdev         // redo allocation 2 times.
149551c0b2f7Stbbdev         for (int idx = MIN_BIN_IDX; idx < MAX_BIN_IDX; ++idx) {
149651c0b2f7Stbbdev             size_t allocationSize = alignedSizeFromIdx(idx);
149751c0b2f7Stbbdev             int localIdx = idx - MIN_BIN_IDX;
149851c0b2f7Stbbdev             loArray[localIdx] = defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize);
149951c0b2f7Stbbdev             REQUIRE_MESSAGE(loArray[localIdx], "Large object was not allocated.");
150051c0b2f7Stbbdev             loc->put(loArray[localIdx]);
150151c0b2f7Stbbdev             loArray[localIdx] = defaultMemPool->extMemPool.mallocLargeObject(defaultMemPool, allocationSize);
150251c0b2f7Stbbdev         }
150351c0b2f7Stbbdev         for (int idx = MIN_BIN_IDX; idx < MAX_BIN_IDX; ++idx) {
150451c0b2f7Stbbdev             loc->put(loArray[idx - MIN_BIN_IDX]);
150551c0b2f7Stbbdev         }
150651c0b2f7Stbbdev     }
150751c0b2f7Stbbdev     void clean(bool all) {
150851c0b2f7Stbbdev         if (all) {
150951c0b2f7Stbbdev             // Should avoid any threshold and clean all bins
151051c0b2f7Stbbdev             loc->cleanAll();
151151c0b2f7Stbbdev         } else {
151251c0b2f7Stbbdev             // Regular cleanup should do nothing for bins above threshold. Decreasing option used
151351c0b2f7Stbbdev             // for the test to be sure that all objects below defaultMaxHugeSize (sieveSize) were cleaned
151451c0b2f7Stbbdev             loc->regularCleanup();
151551c0b2f7Stbbdev             loc->decreasingCleanup();
151651c0b2f7Stbbdev         }
151751c0b2f7Stbbdev     }
151851c0b2f7Stbbdev     void check(CleanupType type) {
151951c0b2f7Stbbdev         for (int idx = MIN_BIN_IDX; idx < MAX_BIN_IDX; ++idx) {
152051c0b2f7Stbbdev             size_t objectSize = alignedSizeFromIdx(idx);
152151c0b2f7Stbbdev             // Cache object below sieve threshold and above huge object threshold should be cached
152251c0b2f7Stbbdev             // (other should be sieved). Unless all cache is dropped. Regular cleanup drops object only below sieve size.
152351c0b2f7Stbbdev             if (type == NO_CLEANUP && sizeInCacheRange(objectSize)) {
152451c0b2f7Stbbdev                 REQUIRE_MESSAGE(objectInCacheBin(idx, objectSize), "Object was released from cache, it shouldn't.");
152551c0b2f7Stbbdev             } else if (type == REGULAR_CLEANUP && (objectSize >= hugeSize)) {
152651c0b2f7Stbbdev                 REQUIRE_MESSAGE(objectInCacheBin(idx, objectSize), "Object was released from cache, it shouldn't.");
152751c0b2f7Stbbdev             } else { // HARD_CLEANUP
152851c0b2f7Stbbdev                 REQUIRE_MESSAGE(cacheBinEmpty(idx), "Object is still cached.");
152951c0b2f7Stbbdev             }
153051c0b2f7Stbbdev         }
153151c0b2f7Stbbdev     }
153251c0b2f7Stbbdev 
153351c0b2f7Stbbdev private:
153451c0b2f7Stbbdev     bool cacheBinEmpty(int idx) {
153551c0b2f7Stbbdev         return (loc->hugeCache.bin[idx].cachedSize == 0 && loc->hugeCache.bin[idx].get() == NULL);
153651c0b2f7Stbbdev     }
153751c0b2f7Stbbdev     bool objectInCacheBin(int idx, size_t size) {
153851c0b2f7Stbbdev         return (loc->hugeCache.bin[idx].cachedSize != 0 && loc->hugeCache.bin[idx].cachedSize % size == 0);
153951c0b2f7Stbbdev     }
154051c0b2f7Stbbdev     bool sizeInCacheRange(size_t size) {
154151c0b2f7Stbbdev         return size <= sieveSize || size >= hugeSize;
154251c0b2f7Stbbdev     }
154351c0b2f7Stbbdev     size_t alignedSizeFromIdx(int idx) {
154451c0b2f7Stbbdev         return rml::internal::LargeObjectCache::alignToBin(hocIdxToSizeFormula(idx));
154551c0b2f7Stbbdev     }
154651c0b2f7Stbbdev };
154751c0b2f7Stbbdev 
154851c0b2f7Stbbdev // TBBMALLOC_SET_HUGE_OBJECT_THRESHOLD value should be set before the test,
154951c0b2f7Stbbdev // through scalable API or env variable
155051c0b2f7Stbbdev void TestHugeSizeThresholdImpl(LargeObjectCache* loc, size_t hugeSize, bool fullTesting) {
155151c0b2f7Stbbdev     HOThresholdTester test = {loc, hugeSize};
155251c0b2f7Stbbdev     test.populateCache();
155351c0b2f7Stbbdev     // Check the default sieve value
155451c0b2f7Stbbdev     test.check(HOThresholdTester::NO_CLEANUP);
155551c0b2f7Stbbdev 
155651c0b2f7Stbbdev     if(fullTesting) {
155751c0b2f7Stbbdev         // Check that objects above threshold stay in cache after regular cleanup
155851c0b2f7Stbbdev         test.clean(/*all*/false);
155951c0b2f7Stbbdev         test.check(HOThresholdTester::REGULAR_CLEANUP);
156051c0b2f7Stbbdev     }
156151c0b2f7Stbbdev     // Check that all objects dropped from cache after hard cleanup (ignore huge obects threshold)
156251c0b2f7Stbbdev     test.clean(/*all*/true);
156351c0b2f7Stbbdev     test.check(HOThresholdTester::HARD_CLEANUP);
156451c0b2f7Stbbdev     // Restore previous settings
156551c0b2f7Stbbdev     loc->setHugeSizeThreshold(LargeObjectCache::maxHugeSize);
156651c0b2f7Stbbdev     loc->reset();
156751c0b2f7Stbbdev }
156851c0b2f7Stbbdev 
156951c0b2f7Stbbdev /*
157051c0b2f7Stbbdev  *  Test for default huge size and behaviour when huge object settings defined
157151c0b2f7Stbbdev  */
157251c0b2f7Stbbdev void TestHugeSizeThreshold() {
157351c0b2f7Stbbdev     // Clean up if something was allocated before the test and reset cache state
157451c0b2f7Stbbdev     scalable_allocation_command(TBBMALLOC_CLEAN_ALL_BUFFERS, 0);
157551c0b2f7Stbbdev     LargeObjectCache* loc = &defaultMemPool->extMemPool.loc;
157651c0b2f7Stbbdev     // Restore default settings just in case
157751c0b2f7Stbbdev     loc->setHugeSizeThreshold(LargeObjectCache::maxHugeSize);
157851c0b2f7Stbbdev     loc->reset();
157951c0b2f7Stbbdev     // Firstly check default huge size value (with max huge object threshold).
158051c0b2f7Stbbdev     // Everything that more then this value should be released to OS without caching.
158151c0b2f7Stbbdev     TestHugeSizeThresholdImpl(loc, loc->hugeSizeThreshold, false);
158251c0b2f7Stbbdev     // Then set huge object threshold.
158351c0b2f7Stbbdev     // All objects with sizes after threshold will be released only after the hard cleanup.
158451c0b2f7Stbbdev #if !__TBB_WIN8UI_SUPPORT
158551c0b2f7Stbbdev     // Unit testing for environment variable
158651c0b2f7Stbbdev     utils::SetEnv("TBB_MALLOC_SET_HUGE_SIZE_THRESHOLD","67108864");
158751c0b2f7Stbbdev     // Large object cache reads threshold environment during initialization.
158851c0b2f7Stbbdev     // Reset the value before the test.
158951c0b2f7Stbbdev     loc->hugeSizeThreshold = 0;
15908dcbd5b1Stbbdev     // Reset logical time to prevent regular cleanup
15918dcbd5b1Stbbdev     loc->cacheCurrTime = 0;
159251c0b2f7Stbbdev     loc->init(&defaultMemPool->extMemPool);
159351c0b2f7Stbbdev     TestHugeSizeThresholdImpl(loc, 64 * MByte, true);
159451c0b2f7Stbbdev #endif
159551c0b2f7Stbbdev     // Unit testing for scalable_allocation_command
159651c0b2f7Stbbdev     scalable_allocation_mode(TBBMALLOC_SET_HUGE_SIZE_THRESHOLD, 56 * MByte);
159751c0b2f7Stbbdev     TestHugeSizeThresholdImpl(loc, 56 * MByte, true);
159851c0b2f7Stbbdev }
159951c0b2f7Stbbdev 
160051c0b2f7Stbbdev //! \brief \ref error_guessing
160151c0b2f7Stbbdev TEST_CASE("Main test case") {
160251c0b2f7Stbbdev     scalable_allocation_mode(USE_HUGE_PAGES, 0);
160351c0b2f7Stbbdev #if !__TBB_WIN8UI_SUPPORT
160451c0b2f7Stbbdev     utils::SetEnv("TBB_MALLOC_USE_HUGE_PAGES","yes");
160551c0b2f7Stbbdev #endif
160651c0b2f7Stbbdev     checkNoHugePages();
160751c0b2f7Stbbdev     // backreference requires that initialization was done
160851c0b2f7Stbbdev     if(!isMallocInitialized()) doInitialization();
160951c0b2f7Stbbdev     checkNoHugePages();
161051c0b2f7Stbbdev     // to succeed, leak detection must be the 1st memory-intensive test
161151c0b2f7Stbbdev     TestBackRef();
161251c0b2f7Stbbdev     TestCleanAllBuffers<4*1024>();
161351c0b2f7Stbbdev     TestCleanAllBuffers<16*1024>();
161451c0b2f7Stbbdev     TestCleanThreadBuffers();
161551c0b2f7Stbbdev     TestPools();
161651c0b2f7Stbbdev     TestBackend();
161751c0b2f7Stbbdev 
161851c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION
161951c0b2f7Stbbdev     for( int p=MaxThread; p>=MinThread; --p ) {
162051c0b2f7Stbbdev         TestStartupAlloc::initBarrier( p );
162151c0b2f7Stbbdev         utils::NativeParallelFor( p, TestStartupAlloc() );
162251c0b2f7Stbbdev         REQUIRE_MESSAGE(!firstStartupBlock, "Startup heap memory leak detected");
162351c0b2f7Stbbdev     }
162451c0b2f7Stbbdev #endif
162551c0b2f7Stbbdev     TestLargeObjectCache();
162651c0b2f7Stbbdev     TestObjectRecognition();
162751c0b2f7Stbbdev     TestBitMask();
162851c0b2f7Stbbdev     TestHeapLimit();
162951c0b2f7Stbbdev     TestLOC();
163051c0b2f7Stbbdev     TestSlabAlignment();
163151c0b2f7Stbbdev }
163251c0b2f7Stbbdev 
163351c0b2f7Stbbdev //! \brief \ref error_guessing
163451c0b2f7Stbbdev TEST_CASE("Decreasing reallocation") {
163551c0b2f7Stbbdev     if (!isMallocInitialized()) doInitialization();
163651c0b2f7Stbbdev     TestReallocDecreasing();
163751c0b2f7Stbbdev }
163851c0b2f7Stbbdev 
163951c0b2f7Stbbdev //! \brief \ref error_guessing
164051c0b2f7Stbbdev TEST_CASE("Large object cache bins converter") {
164151c0b2f7Stbbdev     if (!isMallocInitialized()) doInitialization();
164251c0b2f7Stbbdev     TestLOCacheBinsConverter();
164351c0b2f7Stbbdev }
164451c0b2f7Stbbdev 
164551c0b2f7Stbbdev //! \brief \ref error_guessing
164651c0b2f7Stbbdev TEST_CASE("Huge size threshold settings") {
164751c0b2f7Stbbdev     if (!isMallocInitialized()) doInitialization();
164851c0b2f7Stbbdev     TestHugeSizeThreshold();
164951c0b2f7Stbbdev }
165051c0b2f7Stbbdev 
165151c0b2f7Stbbdev #if __linux__
165251c0b2f7Stbbdev //! \brief \ref error_guessing
165351c0b2f7Stbbdev TEST_CASE("Transparent huge pages") {
165451c0b2f7Stbbdev     if (utils::isTHPEnabledOnMachine()) {
165551c0b2f7Stbbdev         if (!isMallocInitialized()) doInitialization();
165651c0b2f7Stbbdev         TestTHP();
165751c0b2f7Stbbdev     } else {
165851c0b2f7Stbbdev         INFO("Transparent Huge Pages is not supported on the system - skipped the test\n");
165951c0b2f7Stbbdev     }
166051c0b2f7Stbbdev }
166151c0b2f7Stbbdev #endif
166251c0b2f7Stbbdev 
166351c0b2f7Stbbdev #if !__TBB_WIN8UI_SUPPORT && defined(_WIN32)
166451c0b2f7Stbbdev //! \brief \ref error_guessing
166551c0b2f7Stbbdev TEST_CASE("Function replacement log") {
166651c0b2f7Stbbdev     TesFunctionReplacementLog();
166751c0b2f7Stbbdev }
166851c0b2f7Stbbdev #endif
1669