1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #define __TBB_NO_IMPLICIT_LINKAGE 1
18 
19 #include "common/test.h"
20 #include "common/utils.h"
21 #include "common/utils_report.h"
22 #include "common/memory_usage.h"
23 #include "oneapi/tbb/detail/_utils.h"
24 #include "tbb/scalable_allocator.h"
25 #include "thread"
26 #include <stdio.h>
27 
28 class minimalAllocFree {
29 public:
30     void operator()(int size) const {
31         tbb::scalable_allocator<char> a;
32         char* str = a.allocate( size );
33         a.deallocate( str, size );
34     }
35 };
36 
37 
38 template<typename Body, typename Arg>
39 void RunThread(const Body& body, const Arg& arg) {
40     std::thread job(body, arg);
41     job.join();
42 }
43 
44 /*--------------------------------------------------------------------*/
45 // The regression test against bug #1518 where thread bootstrap allocations "leaked"
46 
47 bool TestBootstrapLeak() {
48     /* In the bug 1518, each thread leaked ~384 bytes.
49        Initially, scalable allocator maps 1MB. Thus it is necessary to take out most of this space.
50        1MB is chunked into 16K blocks; of those, one block is for thread bootstrap, and one more
51        should be reserved for the test body. 62 blocks left, each can serve 15 objects of 1024 bytes.
52     */
53     const int alloc_size = 1024;
54     const int take_out_count = 15*62;
55 
56     tbb::scalable_allocator<char> a;
57     char* array[take_out_count];
58     for( int i=0; i<take_out_count; ++i )
59         array[i] = a.allocate( alloc_size );
60 
61     RunThread( minimalAllocFree(), alloc_size ); // for threading library to take some memory
62     size_t memory_in_use = utils::GetMemoryUsage();
63     // Wait for memory usage data to "stabilize". The test number (1000) has nothing underneath.
64     for( int i=0; i<1000; i++) {
65         if( utils::GetMemoryUsage()!=memory_in_use ) {
66             memory_in_use = utils::GetMemoryUsage();
67             i = -1;
68         }
69     }
70 
71     ptrdiff_t memory_leak = 0;
72     // Note that 16K bootstrap memory block is enough to serve 42 threads.
73     const int num_thread_runs = 200;
74     for (;;) {
75         memory_in_use = utils::GetMemoryUsage();
76         for( int i=0; i<num_thread_runs; ++i )
77             RunThread( minimalAllocFree(), alloc_size );
78 
79         memory_leak = utils::GetMemoryUsage() - memory_in_use;
80         if (!memory_leak)
81             break;
82     }
83     if( memory_leak>0 ) { // possibly too strong?
84         REPORT( "Error: memory leak of up to %ld bytes\n", static_cast<long>(memory_leak));
85     }
86 
87     for( int i=0; i<take_out_count; ++i )
88         a.deallocate( array[i], alloc_size );
89 
90     return memory_leak<=0;
91 }
92 
93 /*--------------------------------------------------------------------*/
94 // The regression test against a bug with incompatible semantics of msize and realloc
95 
96 bool TestReallocMsize(size_t startSz) {
97     bool passed = true;
98 
99     char *buf = (char*)scalable_malloc(startSz);
100     REQUIRE_MESSAGE(buf, "");
101     size_t realSz = scalable_msize(buf);
102     REQUIRE_MESSAGE(realSz>=startSz, "scalable_msize must be not less then allocated size");
103     memset(buf, 'a', realSz-1);
104     buf[realSz-1] = 0;
105     char *buf1 = (char*)scalable_realloc(buf, 2*realSz);
106     REQUIRE_MESSAGE(buf1, "");
107     REQUIRE_MESSAGE((scalable_msize(buf1)>=2*realSz), "scalable_msize must be not less then allocated size");
108     buf1[2*realSz-1] = 0;
109     if ( strspn(buf1, "a") < realSz-1 ) {
110         REPORT( "Error: data broken for %d Bytes object.\n", startSz);
111         passed = false;
112     }
113     scalable_free(buf1);
114 
115     return passed;
116 }
117 
118 // regression test against incorrect work of msize/realloc
119 // for aligned objects
120 void TestAlignedMsize()
121 {
122     const int NUM = 4;
123     char *p[NUM];
124     size_t objSizes[NUM];
125     size_t allocSz[] = {4, 8, 512, 2*1024, 4*1024, 8*1024, 16*1024, 0};
126     size_t align[] = {8, 512, 2*1024, 4*1024, 8*1024, 16*1024, 0};
127 
128     for (int a=0; align[a]; a++)
129         for (int s=0; allocSz[s]; s++) {
130             for (int i=0; i<NUM; i++) {
131                 p[i] = (char*)scalable_aligned_malloc(allocSz[s], align[a]);
132                 CHECK_FAST(tbb::detail::is_aligned(p[i], align[a]));
133             }
134 
135             for (int i=0; i<NUM; i++) {
136                 objSizes[i] = scalable_msize(p[i]);
137                 CHECK_FAST_MESSAGE(objSizes[i] >= allocSz[s], "allocated size must be not less than requested");
138                 memset(p[i], i, objSizes[i]);
139             }
140             for (int i=0; i<NUM; i++) {
141                 for (unsigned j=0; j<objSizes[i]; j++)
142                     CHECK_FAST_MESSAGE((((char*)p[i])[j] == i), "Error: data broken");
143             }
144 
145             for (int i=0; i<NUM; i++) {
146                 p[i] = (char*)scalable_aligned_realloc(p[i], 2*allocSz[s], align[a]);
147                 CHECK(tbb::detail::is_aligned(p[i], align[a]));
148                 memset((char*)p[i]+allocSz[s], i+1, allocSz[s]);
149             }
150             for (int i=0; i<NUM; i++) {
151                 for (unsigned j=0; j<allocSz[s]; j++)
152                     CHECK_FAST_MESSAGE((((char*)p[i])[j] == i), "Error: data broken");
153                 for (size_t j=allocSz[s]; j<2*allocSz[s]; j++)
154                     CHECK_FAST_MESSAGE((((char*)p[i])[j] == i+1), "Error: data broken");
155             }
156             for (int i=0; i<NUM; i++)
157                 scalable_free(p[i]);
158         }
159 }
160 
161 //! \brief \ref error_guessing
162 TEST_CASE("testing leaks") {
163     // Check whether memory usage data can be obtained; if not, skip test_bootstrap_leak.
164     if (utils::GetMemoryUsage()) {
165         REQUIRE_MESSAGE(TestBootstrapLeak(), "Test failed" );
166     }
167 }
168 
169 //! \brief \ref error_guessing
170 TEST_CASE("testing realloc mem size") {
171     bool passed = true;
172     // TestReallocMsize runs for each power of 2 and each Fibonacci number below 64K
173     for (size_t a=1, b=1, sum=1; sum<=64*1024; ) {
174         passed &= TestReallocMsize(sum);
175         a = b;
176         b = sum;
177         sum = a+b;
178     }
179     for (size_t a=2; a<=64*1024; a*=2) {
180         passed &= TestReallocMsize(a);
181     }
182     REQUIRE_MESSAGE( passed, "Test failed" );
183 }
184 
185 //! \brief \ref error_guessing
186 TEST_CASE("testing memory align") {
187     TestAlignedMsize();
188 }
189