151c0b2f7Stbbdev /*
2b15aabb3Stbbdev     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 #define __TBB_NO_IMPLICIT_LINKAGE 1
1851c0b2f7Stbbdev 
1951c0b2f7Stbbdev #include "common/test.h"
2051c0b2f7Stbbdev #include "common/utils.h"
2151c0b2f7Stbbdev #include "common/utils_report.h"
2251c0b2f7Stbbdev #include "common/memory_usage.h"
2349e08aacStbbdev #include "oneapi/tbb/detail/_utils.h"
2451c0b2f7Stbbdev #include "tbb/scalable_allocator.h"
2551c0b2f7Stbbdev #include "thread"
2651c0b2f7Stbbdev #include <stdio.h>
2751c0b2f7Stbbdev 
2851c0b2f7Stbbdev class minimalAllocFree {
2951c0b2f7Stbbdev public:
operator ()(int size) const3051c0b2f7Stbbdev     void operator()(int size) const {
3151c0b2f7Stbbdev         tbb::scalable_allocator<char> a;
3251c0b2f7Stbbdev         char* str = a.allocate( size );
3351c0b2f7Stbbdev         a.deallocate( str, size );
3451c0b2f7Stbbdev     }
3551c0b2f7Stbbdev };
3651c0b2f7Stbbdev 
3751c0b2f7Stbbdev 
3851c0b2f7Stbbdev template<typename Body, typename Arg>
RunThread(const Body & body,const Arg & arg)3951c0b2f7Stbbdev void RunThread(const Body& body, const Arg& arg) {
4051c0b2f7Stbbdev     std::thread job(body, arg);
4151c0b2f7Stbbdev     job.join();
4251c0b2f7Stbbdev }
4351c0b2f7Stbbdev 
4451c0b2f7Stbbdev /*--------------------------------------------------------------------*/
4551c0b2f7Stbbdev // The regression test against bug #1518 where thread bootstrap allocations "leaked"
4651c0b2f7Stbbdev 
TestBootstrapLeak()4751c0b2f7Stbbdev bool TestBootstrapLeak() {
4851c0b2f7Stbbdev     /* In the bug 1518, each thread leaked ~384 bytes.
4951c0b2f7Stbbdev        Initially, scalable allocator maps 1MB. Thus it is necessary to take out most of this space.
5051c0b2f7Stbbdev        1MB is chunked into 16K blocks; of those, one block is for thread bootstrap, and one more
5151c0b2f7Stbbdev        should be reserved for the test body. 62 blocks left, each can serve 15 objects of 1024 bytes.
5251c0b2f7Stbbdev     */
5351c0b2f7Stbbdev     const int alloc_size = 1024;
5451c0b2f7Stbbdev     const int take_out_count = 15*62;
5551c0b2f7Stbbdev 
5651c0b2f7Stbbdev     tbb::scalable_allocator<char> a;
5751c0b2f7Stbbdev     char* array[take_out_count];
5851c0b2f7Stbbdev     for( int i=0; i<take_out_count; ++i )
5951c0b2f7Stbbdev         array[i] = a.allocate( alloc_size );
6051c0b2f7Stbbdev 
6151c0b2f7Stbbdev     RunThread( minimalAllocFree(), alloc_size ); // for threading library to take some memory
6251c0b2f7Stbbdev     size_t memory_in_use = utils::GetMemoryUsage();
6351c0b2f7Stbbdev     // Wait for memory usage data to "stabilize". The test number (1000) has nothing underneath.
6451c0b2f7Stbbdev     for( int i=0; i<1000; i++) {
6551c0b2f7Stbbdev         if( utils::GetMemoryUsage()!=memory_in_use ) {
6651c0b2f7Stbbdev             memory_in_use = utils::GetMemoryUsage();
6751c0b2f7Stbbdev             i = -1;
6851c0b2f7Stbbdev         }
6951c0b2f7Stbbdev     }
7051c0b2f7Stbbdev 
7151c0b2f7Stbbdev     ptrdiff_t memory_leak = 0;
7251c0b2f7Stbbdev     // Note that 16K bootstrap memory block is enough to serve 42 threads.
7351c0b2f7Stbbdev     const int num_thread_runs = 200;
74b15aabb3Stbbdev     for (;;) {
7551c0b2f7Stbbdev         memory_in_use = utils::GetMemoryUsage();
7651c0b2f7Stbbdev         for( int i=0; i<num_thread_runs; ++i )
7751c0b2f7Stbbdev             RunThread( minimalAllocFree(), alloc_size );
7851c0b2f7Stbbdev 
7951c0b2f7Stbbdev         memory_leak = utils::GetMemoryUsage() - memory_in_use;
8051c0b2f7Stbbdev         if (!memory_leak)
8151c0b2f7Stbbdev             break;
8251c0b2f7Stbbdev     }
8351c0b2f7Stbbdev     if( memory_leak>0 ) { // possibly too strong?
8451c0b2f7Stbbdev         REPORT( "Error: memory leak of up to %ld bytes\n", static_cast<long>(memory_leak));
8551c0b2f7Stbbdev     }
8651c0b2f7Stbbdev 
8751c0b2f7Stbbdev     for( int i=0; i<take_out_count; ++i )
8851c0b2f7Stbbdev         a.deallocate( array[i], alloc_size );
8951c0b2f7Stbbdev 
9051c0b2f7Stbbdev     return memory_leak<=0;
9151c0b2f7Stbbdev }
9251c0b2f7Stbbdev 
9351c0b2f7Stbbdev /*--------------------------------------------------------------------*/
9451c0b2f7Stbbdev // The regression test against a bug with incompatible semantics of msize and realloc
9551c0b2f7Stbbdev 
TestReallocMsize(size_t startSz)9651c0b2f7Stbbdev bool TestReallocMsize(size_t startSz) {
9751c0b2f7Stbbdev     bool passed = true;
9851c0b2f7Stbbdev 
9951c0b2f7Stbbdev     char *buf = (char*)scalable_malloc(startSz);
10051c0b2f7Stbbdev     REQUIRE_MESSAGE(buf, "");
10151c0b2f7Stbbdev     size_t realSz = scalable_msize(buf);
10251c0b2f7Stbbdev     REQUIRE_MESSAGE(realSz>=startSz, "scalable_msize must be not less then allocated size");
10351c0b2f7Stbbdev     memset(buf, 'a', realSz-1);
10451c0b2f7Stbbdev     buf[realSz-1] = 0;
10551c0b2f7Stbbdev     char *buf1 = (char*)scalable_realloc(buf, 2*realSz);
10651c0b2f7Stbbdev     REQUIRE_MESSAGE(buf1, "");
10751c0b2f7Stbbdev     REQUIRE_MESSAGE((scalable_msize(buf1)>=2*realSz), "scalable_msize must be not less then allocated size");
10851c0b2f7Stbbdev     buf1[2*realSz-1] = 0;
10951c0b2f7Stbbdev     if ( strspn(buf1, "a") < realSz-1 ) {
11051c0b2f7Stbbdev         REPORT( "Error: data broken for %d Bytes object.\n", startSz);
11151c0b2f7Stbbdev         passed = false;
11251c0b2f7Stbbdev     }
11351c0b2f7Stbbdev     scalable_free(buf1);
11451c0b2f7Stbbdev 
11551c0b2f7Stbbdev     return passed;
11651c0b2f7Stbbdev }
11751c0b2f7Stbbdev 
11851c0b2f7Stbbdev // regression test against incorrect work of msize/realloc
11951c0b2f7Stbbdev // for aligned objects
TestAlignedMsize()12051c0b2f7Stbbdev void TestAlignedMsize()
12151c0b2f7Stbbdev {
12251c0b2f7Stbbdev     const int NUM = 4;
12351c0b2f7Stbbdev     char *p[NUM];
12451c0b2f7Stbbdev     size_t objSizes[NUM];
12551c0b2f7Stbbdev     size_t allocSz[] = {4, 8, 512, 2*1024, 4*1024, 8*1024, 16*1024, 0};
12651c0b2f7Stbbdev     size_t align[] = {8, 512, 2*1024, 4*1024, 8*1024, 16*1024, 0};
12751c0b2f7Stbbdev 
12851c0b2f7Stbbdev     for (int a=0; align[a]; a++)
12951c0b2f7Stbbdev         for (int s=0; allocSz[s]; s++) {
13051c0b2f7Stbbdev             for (int i=0; i<NUM; i++) {
13151c0b2f7Stbbdev                 p[i] = (char*)scalable_aligned_malloc(allocSz[s], align[a]);
132b15aabb3Stbbdev                 CHECK_FAST(tbb::detail::is_aligned(p[i], align[a]));
13351c0b2f7Stbbdev             }
13451c0b2f7Stbbdev 
13551c0b2f7Stbbdev             for (int i=0; i<NUM; i++) {
13651c0b2f7Stbbdev                 objSizes[i] = scalable_msize(p[i]);
137b15aabb3Stbbdev                 CHECK_FAST_MESSAGE(objSizes[i] >= allocSz[s], "allocated size must be not less than requested");
13851c0b2f7Stbbdev                 memset(p[i], i, objSizes[i]);
13951c0b2f7Stbbdev             }
14051c0b2f7Stbbdev             for (int i=0; i<NUM; i++) {
14151c0b2f7Stbbdev                 for (unsigned j=0; j<objSizes[i]; j++)
142b15aabb3Stbbdev                     CHECK_FAST_MESSAGE((((char*)p[i])[j] == i), "Error: data broken");
14351c0b2f7Stbbdev             }
14451c0b2f7Stbbdev 
14551c0b2f7Stbbdev             for (int i=0; i<NUM; i++) {
14651c0b2f7Stbbdev                 p[i] = (char*)scalable_aligned_realloc(p[i], 2*allocSz[s], align[a]);
14751c0b2f7Stbbdev                 CHECK(tbb::detail::is_aligned(p[i], align[a]));
14851c0b2f7Stbbdev                 memset((char*)p[i]+allocSz[s], i+1, allocSz[s]);
14951c0b2f7Stbbdev             }
15051c0b2f7Stbbdev             for (int i=0; i<NUM; i++) {
15151c0b2f7Stbbdev                 for (unsigned j=0; j<allocSz[s]; j++)
152b15aabb3Stbbdev                     CHECK_FAST_MESSAGE((((char*)p[i])[j] == i), "Error: data broken");
15351c0b2f7Stbbdev                 for (size_t j=allocSz[s]; j<2*allocSz[s]; j++)
154b15aabb3Stbbdev                     CHECK_FAST_MESSAGE((((char*)p[i])[j] == i+1), "Error: data broken");
15551c0b2f7Stbbdev             }
15651c0b2f7Stbbdev             for (int i=0; i<NUM; i++)
15751c0b2f7Stbbdev                 scalable_free(p[i]);
15851c0b2f7Stbbdev         }
15951c0b2f7Stbbdev }
16051c0b2f7Stbbdev 
161*94b62cc1Svlserov #if __TBB_USE_ADDRESS_SANITIZER
162*94b62cc1Svlserov //! \brief \ref error_guessing
skip(true)163*94b62cc1Svlserov TEST_CASE("Memory leaks test is not applicable under ASAN\n" * doctest::skip(true)) {}
164*94b62cc1Svlserov #else
16551c0b2f7Stbbdev //! \brief \ref error_guessing
16651c0b2f7Stbbdev TEST_CASE("testing leaks") {
16751c0b2f7Stbbdev     // Check whether memory usage data can be obtained; if not, skip test_bootstrap_leak.
16851c0b2f7Stbbdev     if (utils::GetMemoryUsage()) {
16951c0b2f7Stbbdev         REQUIRE_MESSAGE(TestBootstrapLeak(), "Test failed");
17051c0b2f7Stbbdev     }
17151c0b2f7Stbbdev }
172*94b62cc1Svlserov #endif // __TBB_USE_ADDRESS_SANITIZER
17351c0b2f7Stbbdev 
17451c0b2f7Stbbdev //! \brief \ref error_guessing
17551c0b2f7Stbbdev TEST_CASE("testing realloc mem size") {
17651c0b2f7Stbbdev     bool passed = true;
17751c0b2f7Stbbdev     // TestReallocMsize runs for each power of 2 and each Fibonacci number below 64K
17851c0b2f7Stbbdev     for (size_t a=1, b=1, sum=1; sum<=64*1024; ) {
17951c0b2f7Stbbdev         passed &= TestReallocMsize(sum);
18051c0b2f7Stbbdev         a = b;
18151c0b2f7Stbbdev         b = sum;
18251c0b2f7Stbbdev         sum = a+b;
18351c0b2f7Stbbdev     }
18451c0b2f7Stbbdev     for (size_t a=2; a<=64*1024; a*=2) {
18551c0b2f7Stbbdev         passed &= TestReallocMsize(a);
18651c0b2f7Stbbdev     }
18751c0b2f7Stbbdev     REQUIRE_MESSAGE( passed, "Test failed" );
18851c0b2f7Stbbdev }
18951c0b2f7Stbbdev 
19051c0b2f7Stbbdev //! \brief \ref error_guessing
19151c0b2f7Stbbdev TEST_CASE("testing memory align") {
19251c0b2f7Stbbdev     TestAlignedMsize();
19351c0b2f7Stbbdev }
194