1 /*
2    Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software Foundation,
22    51 Franklin Street, Suite 500, Boston, MA 02110-1301 USA */
23 
24 // First include (the generated) my_config.h, to get correct platform defines.
25 #include "my_config.h"
26 #include <gtest/gtest.h>
27 #include "mysys_err.h"
28 
29 #include "my_sys.h"
30 
31 extern "C" void mock_error_handler_hook(uint err, const char *str, myf MyFlags);
32 
33 /**
34   An alternative error_handler for non-server unit tests since it does
35   not rely on THD.  It sets the global error handler function.
36 */
37 class Mock_global_error_handler
38 {
39 public:
Mock_global_error_handler(uint expected_error)40   Mock_global_error_handler(uint expected_error):
41     m_expected_error(expected_error),
42     m_handle_called(0)
43   {
44     current= this;
45     m_old_error_handler_hook= error_handler_hook;
46     error_handler_hook = mock_error_handler_hook;
47   }
48 
~Mock_global_error_handler()49   virtual ~Mock_global_error_handler()
50   {
51     if (m_expected_error == 0)
52     {
53       EXPECT_EQ(0, m_handle_called);
54     }
55     else
56     {
57       EXPECT_GT(m_handle_called, 0);
58     }
59     error_handler_hook= m_old_error_handler_hook;
60     current= NULL;
61   }
62 
error_handler(uint err)63   void error_handler(uint err)
64   {
65     EXPECT_EQ(m_expected_error, err);
66     ++m_handle_called;
67   }
68 
handle_called() const69   int handle_called() const { return m_handle_called; }
70 
71   static Mock_global_error_handler *current;
72 
73 private:
74   uint m_expected_error;
75   int  m_handle_called;
76 
77   void (*m_old_error_handler_hook)(uint, const char *, myf);
78 };
79 
80 Mock_global_error_handler *Mock_global_error_handler::current= NULL;
81 
82 /*
83   Error handler function.
84 */
mock_error_handler_hook(uint err,const char * str,myf MyFlags)85 extern "C" void mock_error_handler_hook(uint err, const char *str, myf MyFlags)
86 {
87   if (Mock_global_error_handler::current)
88     Mock_global_error_handler::current->error_handler(err);
89 }
90 
91 namespace my_alloc_unittest {
92 
93 const size_t num_iterations= 1ULL;
94 
95 class MyAllocTest : public ::testing::TestWithParam<size_t>
96 {
97 protected:
SetUp()98   virtual void SetUp()
99   {
100     init_alloc_root(PSI_NOT_INSTRUMENTED, &m_root, 1024, 0);
101   }
TearDown()102   virtual void TearDown()
103   {
104     free_root(&m_root, MYF(0));
105   }
106   size_t   m_num_objects;
107   MEM_ROOT m_root;
108 };
109 
110 class MyPreAllocTest : public ::testing::Test
111 {
112 protected:
SetUp()113   virtual void SetUp()
114   {
115     init_alloc_root(PSI_NOT_INSTRUMENTED, &m_prealloc_root, 1024, 2048);
116   }
TearDown()117   virtual void TearDown()
118   {
119     free_root(&m_prealloc_root, MYF(0));
120   }
121   size_t   m_num_objects;
122   MEM_ROOT m_prealloc_root;
123 };
124 
125 
126 
127 size_t test_values[]= {100, 1000, 10000, 100000 };
128 
129 INSTANTIATE_TEST_CASE_P(MyAlloc, MyAllocTest,
130                         ::testing::ValuesIn(test_values));
131 
TEST_P(MyAllocTest,NoMemoryLimit)132 TEST_P(MyAllocTest, NoMemoryLimit)
133 {
134   m_num_objects= GetParam();
135   for (size_t ix= 0; ix < num_iterations; ++ix)
136   {
137     for (size_t objcount= 0; objcount < m_num_objects; ++objcount)
138       alloc_root(&m_root, 100);
139   }
140 }
141 
TEST_P(MyAllocTest,WithMemoryLimit)142 TEST_P(MyAllocTest, WithMemoryLimit)
143 {
144   m_num_objects= GetParam();
145   set_memroot_max_capacity(&m_root, num_iterations * m_num_objects * 100);
146   for (size_t ix= 0; ix < num_iterations; ++ix)
147   {
148     for (size_t objcount= 0; objcount < m_num_objects; ++objcount)
149       alloc_root(&m_root, 100);
150   }
151 }
152 
TEST_F(MyAllocTest,CheckErrorReporting)153 TEST_F(MyAllocTest, CheckErrorReporting)
154 {
155   const void *null_pointer= NULL;
156   EXPECT_TRUE(alloc_root(&m_root, 1000));
157   set_memroot_max_capacity(&m_root, 100);
158   EXPECT_EQ(null_pointer, alloc_root(&m_root, 1000));
159   set_memroot_error_reporting(&m_root, true);
160   Mock_global_error_handler error_handler(EE_CAPACITY_EXCEEDED);
161   EXPECT_TRUE(alloc_root(&m_root, 1000));
162   EXPECT_EQ(1, error_handler.handle_called());
163 }
164 
TEST_F(MyPreAllocTest,PreAlloc)165 TEST_F(MyPreAllocTest, PreAlloc)
166 {
167   // PREALLOCATE_MEMORY_CHUNKS is not defined for valgrind and ASAN
168 #if !defined(HAVE_VALGRIND) && !defined(HAVE_ASAN)
169   const void *null_pointer= NULL;
170   // MEMROOT has pre-allocated 2048 bytes memory plus some overhead
171   size_t pre_allocated= m_prealloc_root.allocated_size;
172   EXPECT_LT((unsigned int)2048, pre_allocated);
173 
174   // This will eat of pre-allocated memory, no more should be allocated
175   EXPECT_TRUE(alloc_root(&m_prealloc_root, 1000));
176   EXPECT_EQ(pre_allocated, m_prealloc_root.allocated_size);
177 
178   set_memroot_max_capacity(&m_prealloc_root, 100);
179   // Sufficient memory has been pre-allocated, so first alloc below will succeed
180   EXPECT_TRUE(alloc_root(&m_prealloc_root, 1000));
181   EXPECT_EQ(null_pointer, alloc_root(&m_prealloc_root, 100));
182   EXPECT_EQ(pre_allocated, m_prealloc_root.allocated_size);
183 
184   // Setting error reporting. Error is flagged but allocation succeeds
185   set_memroot_error_reporting(&m_prealloc_root, true);
186   {
187     Mock_global_error_handler error_handler(EE_CAPACITY_EXCEEDED);
188     EXPECT_TRUE(alloc_root(&m_prealloc_root, 1000));
189     EXPECT_EQ(1, error_handler.handle_called());
190     EXPECT_LT(pre_allocated, m_prealloc_root.allocated_size);
191     pre_allocated= m_prealloc_root.allocated_size;
192   }
193   set_memroot_error_reporting(&m_prealloc_root, false);
194 
195   //This will just mark the blocks free.
196   free_root(&m_prealloc_root, MY_MARK_BLOCKS_FREE);
197   EXPECT_EQ(pre_allocated, m_prealloc_root.allocated_size);
198 
199   set_memroot_max_capacity(&m_prealloc_root, 2048);
200   reset_root_defaults(&m_prealloc_root, 1024, 0);
201   EXPECT_EQ(pre_allocated, m_prealloc_root.allocated_size);
202   reset_root_defaults(&m_prealloc_root, 1024, 1024);
203   EXPECT_LT((unsigned int)1024, m_prealloc_root.allocated_size);
204 
205   reset_root_defaults(&m_prealloc_root, 512, 1024);
206   EXPECT_LT((unsigned int)1024, m_prealloc_root.allocated_size);
207   pre_allocated= m_prealloc_root.allocated_size;
208   // This allocation will use pre-alocated memory
209   EXPECT_TRUE(alloc_root(&m_prealloc_root, 1024));
210   EXPECT_EQ(pre_allocated, m_prealloc_root.allocated_size);
211   // Will allocate more memory
212   EXPECT_TRUE(alloc_root(&m_prealloc_root, 512));
213   EXPECT_LT((unsigned int)1526, m_prealloc_root.allocated_size);
214   pre_allocated= m_prealloc_root.allocated_size;
215   //  This will not succeed
216   EXPECT_EQ(null_pointer, alloc_root(&m_prealloc_root, 512));
217 
218   free_root(&m_prealloc_root, MY_KEEP_PREALLOC);
219   EXPECT_LT((unsigned int)1024, m_prealloc_root.allocated_size);
220 
221   // Specified pre_alloc_size is above capacity. Expect no pre-allocation
222   reset_root_defaults(&m_prealloc_root, 512, 4096);
223   EXPECT_EQ((unsigned int)0, m_prealloc_root.allocated_size);
224 
225   free_root(&m_prealloc_root, 0);
226   EXPECT_EQ((unsigned int)0, m_prealloc_root.allocated_size);
227 #endif
228 }
229 
230 }
231