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