1 /*
2     Copyright (c) 2005-2020 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 HARNESS_NO_PARSE_COMMAND_LINE 1
18 
19 #include <stdio.h>
20 #include "tbb/scalable_allocator.h"
21 
22 class minimalAllocFree {
23 public:
operator ()(int size) const24     void operator()(int size) const {
25         tbb::scalable_allocator<char> a;
26         char* str = a.allocate( size );
27         a.deallocate( str, size );
28     }
29 };
30 
31 #define HARNESS_TBBMALLOC_THREAD_SHUTDOWN 1
32 #include "harness.h"
33 
34 template<typename Body, typename Arg>
RunThread(const Body & body,const Arg & arg)35 void RunThread(const Body& body, const Arg& arg) {
36     NativeParallelForTask<Arg,Body> job(arg, body);
37     job.start();
38     job.wait_to_finish();
39 }
40 
41 /*--------------------------------------------------------------------*/
42 // The regression test against bug #1518 where thread bootstrap allocations "leaked"
43 
44 #include "harness_memory.h"
45 
TestBootstrapLeak()46 bool TestBootstrapLeak() {
47     /* In the bug 1518, each thread leaked ~384 bytes.
48        Initially, scalable allocator maps 1MB. Thus it is necessary to take out most of this space.
49        1MB is chunked into 16K blocks; of those, one block is for thread bootstrap, and one more
50        should be reserved for the test body. 62 blocks left, each can serve 15 objects of 1024 bytes.
51     */
52     const int alloc_size = 1024;
53     const int take_out_count = 15*62;
54 
55     tbb::scalable_allocator<char> a;
56     char* array[take_out_count];
57     for( int i=0; i<take_out_count; ++i )
58         array[i] = a.allocate( alloc_size );
59 
60     RunThread( minimalAllocFree(), alloc_size ); // for threading library to take some memory
61     size_t memory_in_use = GetMemoryUsage();
62     // Wait for memory usage data to "stabilize". The test number (1000) has nothing underneath.
63     for( int i=0; i<1000; i++) {
64         if( GetMemoryUsage()!=memory_in_use ) {
65             memory_in_use = GetMemoryUsage();
66             i = -1;
67         }
68     }
69 
70     ptrdiff_t memory_leak = 0;
71     // Note that 16K bootstrap memory block is enough to serve 42 threads.
72     const int num_thread_runs = 200;
73     for (int run=0; run<3; run++) {
74         memory_in_use = GetMemoryUsage();
75         for( int i=0; i<num_thread_runs; ++i )
76             RunThread( minimalAllocFree(), alloc_size );
77 
78         memory_leak = GetMemoryUsage() - memory_in_use;
79         if (!memory_leak)
80             break;
81     }
82     if( memory_leak>0 ) { // possibly too strong?
83         REPORT( "Error: memory leak of up to %ld bytes\n", static_cast<long>(memory_leak));
84     }
85 
86     for( int i=0; i<take_out_count; ++i )
87         a.deallocate( array[i], alloc_size );
88 
89     return memory_leak<=0;
90 }
91 
92 /*--------------------------------------------------------------------*/
93 // The regression test against a bug with incompatible semantics of msize and realloc
94 
TestReallocMsize(size_t startSz)95 bool TestReallocMsize(size_t startSz) {
96     bool passed = true;
97 
98     char *buf = (char*)scalable_malloc(startSz);
99     ASSERT(buf, "");
100     size_t realSz = scalable_msize(buf);
101     ASSERT(realSz>=startSz, "scalable_msize must be not less then allocated size");
102     memset(buf, 'a', realSz-1);
103     buf[realSz-1] = 0;
104     char *buf1 = (char*)scalable_realloc(buf, 2*realSz);
105     ASSERT(buf1, "");
106     ASSERT(scalable_msize(buf1)>=2*realSz,
107            "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
TestAlignedMsize()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                 ASSERT(is_aligned(p[i], align[a]), NULL);
133             }
134 
135             for (int i=0; i<NUM; i++) {
136                 objSizes[i] = scalable_msize(p[i]);
137                 ASSERT(objSizes[i] >= allocSz[s],
138                        "allocated size must be not less than requested");
139                 memset(p[i], i, objSizes[i]);
140             }
141             for (int i=0; i<NUM; i++) {
142                 for (unsigned j=0; j<objSizes[i]; j++)
143                     ASSERT(((char*)p[i])[j] == i, "Error: data broken");
144             }
145 
146             for (int i=0; i<NUM; i++) {
147                 p[i] = (char*)scalable_aligned_realloc(p[i], 2*allocSz[s], align[a]);
148                 ASSERT(is_aligned(p[i], align[a]), NULL);
149                 memset((char*)p[i]+allocSz[s], i+1, allocSz[s]);
150             }
151             for (int i=0; i<NUM; i++) {
152                 for (unsigned j=0; j<allocSz[s]; j++)
153                     ASSERT(((char*)p[i])[j] == i, "Error: data broken");
154                 for (size_t j=allocSz[s]; j<2*allocSz[s]; j++)
155                     ASSERT(((char*)p[i])[j] == i+1, "Error: data broken");
156             }
157             for (int i=0; i<NUM; i++)
158                 scalable_free(p[i]);
159         }
160 }
161 
162 /*--------------------------------------------------------------------*/
163 // The main test function
164 
TestMain()165 int TestMain () {
166     bool passed = true;
167     // Check whether memory usage data can be obtained; if not, skip test_bootstrap_leak.
168     if( GetMemoryUsage() )
169         passed &= TestBootstrapLeak();
170 
171     // TestReallocMsize runs for each power of 2 and each Fibonacci number below 64K
172     for (size_t a=1, b=1, sum=1; sum<=64*1024; ) {
173         passed &= TestReallocMsize(sum);
174         a = b;
175         b = sum;
176         sum = a+b;
177     }
178     for (size_t a=2; a<=64*1024; a*=2)
179         passed &= TestReallocMsize(a);
180 
181     ASSERT( passed, "Test failed" );
182 
183     TestAlignedMsize();
184 
185     return Harness::Done;
186 }
187