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