1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, 2018, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /********************************************************************//** |
21 | @file mem/mem0mem.cc |
22 | The memory management |
23 | |
24 | Created 6/9/1994 Heikki Tuuri |
25 | *************************************************************************/ |
26 | |
27 | #include "ha_prototypes.h" |
28 | |
29 | #include "mem0mem.h" |
30 | #include "buf0buf.h" |
31 | #include "srv0srv.h" |
32 | #include <stdarg.h> |
33 | |
34 | /**********************************************************************//** |
35 | Concatenate two strings and return the result, using a memory heap. |
36 | @return own: the result */ |
37 | char* |
38 | mem_heap_strcat( |
39 | /*============*/ |
40 | mem_heap_t* heap, /*!< in: memory heap where string is allocated */ |
41 | const char* s1, /*!< in: string 1 */ |
42 | const char* s2) /*!< in: string 2 */ |
43 | { |
44 | char* s; |
45 | ulint s1_len = strlen(s1); |
46 | ulint s2_len = strlen(s2); |
47 | |
48 | s = static_cast<char*>(mem_heap_alloc(heap, s1_len + s2_len + 1)); |
49 | |
50 | memcpy(s, s1, s1_len); |
51 | memcpy(s + s1_len, s2, s2_len); |
52 | |
53 | s[s1_len + s2_len] = '\0'; |
54 | |
55 | return(s); |
56 | } |
57 | |
58 | |
59 | /****************************************************************//** |
60 | Helper function for mem_heap_printf. |
61 | @return length of formatted string, including terminating NUL */ |
62 | static |
63 | ulint |
64 | mem_heap_printf_low( |
65 | /*================*/ |
66 | char* buf, /*!< in/out: buffer to store formatted string |
67 | in, or NULL to just calculate length */ |
68 | const char* format, /*!< in: format string */ |
69 | va_list ap) /*!< in: arguments */ |
70 | { |
71 | ulint len = 0; |
72 | |
73 | while (*format) { |
74 | |
75 | /* Does this format specifier have the 'l' length modifier. */ |
76 | ibool is_long = FALSE; |
77 | |
78 | /* Length of one parameter. */ |
79 | size_t plen; |
80 | |
81 | if (*format++ != '%') { |
82 | /* Non-format character. */ |
83 | |
84 | len++; |
85 | |
86 | if (buf) { |
87 | *buf++ = *(format - 1); |
88 | } |
89 | |
90 | continue; |
91 | } |
92 | |
93 | if (*format == 'l') { |
94 | is_long = TRUE; |
95 | format++; |
96 | } |
97 | |
98 | switch (*format++) { |
99 | case 's': |
100 | /* string */ |
101 | { |
102 | char* s = va_arg(ap, char*); |
103 | |
104 | /* "%ls" is a non-sensical format specifier. */ |
105 | ut_a(!is_long); |
106 | |
107 | plen = strlen(s); |
108 | len += plen; |
109 | |
110 | if (buf) { |
111 | memcpy(buf, s, plen); |
112 | buf += plen; |
113 | } |
114 | } |
115 | |
116 | break; |
117 | |
118 | case 'u': |
119 | /* unsigned int */ |
120 | { |
121 | char tmp[32]; |
122 | unsigned long val; |
123 | |
124 | /* We only support 'long' values for now. */ |
125 | ut_a(is_long); |
126 | |
127 | val = va_arg(ap, unsigned long); |
128 | |
129 | plen = size_t(sprintf(tmp, "%lu" , val)); |
130 | len += plen; |
131 | |
132 | if (buf) { |
133 | memcpy(buf, tmp, plen); |
134 | buf += plen; |
135 | } |
136 | } |
137 | |
138 | break; |
139 | |
140 | case '%': |
141 | |
142 | /* "%l%" is a non-sensical format specifier. */ |
143 | ut_a(!is_long); |
144 | |
145 | len++; |
146 | |
147 | if (buf) { |
148 | *buf++ = '%'; |
149 | } |
150 | |
151 | break; |
152 | |
153 | default: |
154 | ut_error; |
155 | } |
156 | } |
157 | |
158 | /* For the NUL character. */ |
159 | len++; |
160 | |
161 | if (buf) { |
162 | *buf = '\0'; |
163 | } |
164 | |
165 | return(len); |
166 | } |
167 | |
168 | /****************************************************************//** |
169 | A simple sprintf replacement that dynamically allocates the space for the |
170 | formatted string from the given heap. This supports a very limited set of |
171 | the printf syntax: types 's' and 'u' and length modifier 'l' (which is |
172 | required for the 'u' type). |
173 | @return heap-allocated formatted string */ |
174 | char* |
175 | mem_heap_printf( |
176 | /*============*/ |
177 | mem_heap_t* heap, /*!< in: memory heap */ |
178 | const char* format, /*!< in: format string */ |
179 | ...) |
180 | { |
181 | va_list ap; |
182 | char* str; |
183 | ulint len; |
184 | |
185 | /* Calculate length of string */ |
186 | len = 0; |
187 | va_start(ap, format); |
188 | len = mem_heap_printf_low(NULL, format, ap); |
189 | va_end(ap); |
190 | |
191 | /* Now create it for real. */ |
192 | str = static_cast<char*>(mem_heap_alloc(heap, len)); |
193 | va_start(ap, format); |
194 | mem_heap_printf_low(str, format, ap); |
195 | va_end(ap); |
196 | |
197 | return(str); |
198 | } |
199 | |
200 | #ifdef UNIV_DEBUG |
201 | /** Validates the contents of a memory heap. |
202 | Checks a memory heap for consistency, prints the contents if any error |
203 | is detected. A fatal error is logged if an error is detected. |
204 | @param[in] heap Memory heap to validate. */ |
205 | void |
206 | mem_heap_validate( |
207 | const mem_heap_t* heap) |
208 | { |
209 | ulint size = 0; |
210 | |
211 | for (const mem_block_t* block = heap; |
212 | block != NULL; |
213 | block = UT_LIST_GET_NEXT(list, block)) { |
214 | |
215 | mem_block_validate(block); |
216 | |
217 | switch (block->type) { |
218 | case MEM_HEAP_DYNAMIC: |
219 | break; |
220 | case MEM_HEAP_BUFFER: |
221 | case MEM_HEAP_BUFFER | MEM_HEAP_BTR_SEARCH: |
222 | ut_ad(block->len <= srv_page_size); |
223 | break; |
224 | default: |
225 | ut_error; |
226 | } |
227 | |
228 | size += block->len; |
229 | } |
230 | |
231 | ut_ad(size == heap->total_size); |
232 | } |
233 | #endif /* UNIV_DEBUG */ |
234 | |
235 | /***************************************************************//** |
236 | Creates a memory heap block where data can be allocated. |
237 | @return own: memory heap block, NULL if did not succeed (only possible |
238 | for MEM_HEAP_BTR_SEARCH type heaps) */ |
239 | mem_block_t* |
240 | mem_heap_create_block_func( |
241 | /*=======================*/ |
242 | mem_heap_t* heap, /*!< in: memory heap or NULL if first block |
243 | should be created */ |
244 | ulint n, /*!< in: number of bytes needed for user data */ |
245 | #ifdef UNIV_DEBUG |
246 | const char* file_name,/*!< in: file name where created */ |
247 | unsigned line, /*!< in: line where created */ |
248 | #endif /* UNIV_DEBUG */ |
249 | ulint type) /*!< in: type of heap: MEM_HEAP_DYNAMIC or |
250 | MEM_HEAP_BUFFER */ |
251 | { |
252 | buf_block_t* buf_block = NULL; |
253 | mem_block_t* block; |
254 | ulint len; |
255 | |
256 | ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER) |
257 | || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH)); |
258 | |
259 | if (heap != NULL) { |
260 | mem_block_validate(heap); |
261 | ut_d(mem_heap_validate(heap)); |
262 | } |
263 | |
264 | /* In dynamic allocation, calculate the size: block header + data. */ |
265 | len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n); |
266 | |
267 | if (type == MEM_HEAP_DYNAMIC || len < srv_page_size / 2) { |
268 | |
269 | ut_ad(type == MEM_HEAP_DYNAMIC || n <= MEM_MAX_ALLOC_IN_BUF); |
270 | |
271 | block = static_cast<mem_block_t*>(ut_malloc_nokey(len)); |
272 | } else { |
273 | len = srv_page_size; |
274 | |
275 | if ((type & MEM_HEAP_BTR_SEARCH) && heap) { |
276 | /* We cannot allocate the block from the |
277 | buffer pool, but must get the free block from |
278 | the heap header free block field */ |
279 | |
280 | buf_block = static_cast<buf_block_t*>(heap->free_block); |
281 | heap->free_block = NULL; |
282 | |
283 | if (UNIV_UNLIKELY(!buf_block)) { |
284 | |
285 | return(NULL); |
286 | } |
287 | } else { |
288 | buf_block = buf_block_alloc(NULL); |
289 | } |
290 | |
291 | block = (mem_block_t*) buf_block->frame; |
292 | } |
293 | |
294 | if (block == NULL) { |
295 | ib::fatal() << "Unable to allocate memory of size " |
296 | << len << "." ; |
297 | } |
298 | |
299 | block->buf_block = buf_block; |
300 | block->free_block = NULL; |
301 | |
302 | block->magic_n = MEM_BLOCK_MAGIC_N; |
303 | ut_d(ut_strlcpy_rev(block->file_name, file_name, |
304 | sizeof(block->file_name))); |
305 | ut_d(block->line = line); |
306 | |
307 | mem_block_set_len(block, len); |
308 | mem_block_set_type(block, type); |
309 | mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE); |
310 | mem_block_set_start(block, MEM_BLOCK_HEADER_SIZE); |
311 | |
312 | if (UNIV_UNLIKELY(heap == NULL)) { |
313 | /* This is the first block of the heap. The field |
314 | total_size should be initialized here */ |
315 | block->total_size = len; |
316 | } else { |
317 | /* Not the first allocation for the heap. This block's |
318 | total_length field should be set to undefined. */ |
319 | ut_d(block->total_size = ULINT_UNDEFINED); |
320 | UNIV_MEM_INVALID(&block->total_size, |
321 | sizeof block->total_size); |
322 | |
323 | heap->total_size += len; |
324 | } |
325 | |
326 | /* Poison all available memory. Individual chunks will be unpoisoned on |
327 | every mem_heap_alloc() call. */ |
328 | compile_time_assert(MEM_BLOCK_HEADER_SIZE >= sizeof *block); |
329 | UNIV_MEM_FREE(block + 1, len - sizeof *block); |
330 | |
331 | ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len); |
332 | |
333 | return(block); |
334 | } |
335 | |
336 | /***************************************************************//** |
337 | Adds a new block to a memory heap. |
338 | @return created block, NULL if did not succeed (only possible for |
339 | MEM_HEAP_BTR_SEARCH type heaps) */ |
340 | mem_block_t* |
341 | mem_heap_add_block( |
342 | /*===============*/ |
343 | mem_heap_t* heap, /*!< in: memory heap */ |
344 | ulint n) /*!< in: number of bytes user needs */ |
345 | { |
346 | mem_block_t* block; |
347 | mem_block_t* new_block; |
348 | ulint new_size; |
349 | |
350 | ut_d(mem_block_validate(heap)); |
351 | |
352 | block = UT_LIST_GET_LAST(heap->base); |
353 | |
354 | /* We have to allocate a new block. The size is always at least |
355 | doubled until the standard size is reached. After that the size |
356 | stays the same, except in cases where the caller needs more space. */ |
357 | |
358 | new_size = 2 * mem_block_get_len(block); |
359 | |
360 | if (heap->type != MEM_HEAP_DYNAMIC) { |
361 | /* From the buffer pool we allocate buffer frames */ |
362 | ut_a(n <= MEM_MAX_ALLOC_IN_BUF); |
363 | |
364 | if (new_size > MEM_MAX_ALLOC_IN_BUF) { |
365 | new_size = MEM_MAX_ALLOC_IN_BUF; |
366 | } |
367 | } else if (new_size > MEM_BLOCK_STANDARD_SIZE) { |
368 | |
369 | new_size = MEM_BLOCK_STANDARD_SIZE; |
370 | } |
371 | |
372 | if (new_size < n) { |
373 | new_size = n; |
374 | } |
375 | |
376 | new_block = mem_heap_create_block(heap, new_size, heap->type, |
377 | heap->file_name, heap->line); |
378 | if (new_block == NULL) { |
379 | |
380 | return(NULL); |
381 | } |
382 | |
383 | /* Add the new block as the last block */ |
384 | |
385 | UT_LIST_INSERT_AFTER(heap->base, block, new_block); |
386 | |
387 | return(new_block); |
388 | } |
389 | |
390 | /******************************************************************//** |
391 | Frees a block from a memory heap. */ |
392 | void |
393 | mem_heap_block_free( |
394 | /*================*/ |
395 | mem_heap_t* heap, /*!< in: heap */ |
396 | mem_block_t* block) /*!< in: block to free */ |
397 | { |
398 | ulint type; |
399 | ulint len; |
400 | buf_block_t* buf_block; |
401 | |
402 | buf_block = static_cast<buf_block_t*>(block->buf_block); |
403 | |
404 | mem_block_validate(block); |
405 | |
406 | UT_LIST_REMOVE(heap->base, block); |
407 | |
408 | ut_ad(heap->total_size >= block->len); |
409 | heap->total_size -= block->len; |
410 | |
411 | type = heap->type; |
412 | len = block->len; |
413 | block->magic_n = MEM_FREED_BLOCK_MAGIC_N; |
414 | |
415 | if (type == MEM_HEAP_DYNAMIC || len < srv_page_size / 2) { |
416 | ut_ad(!buf_block); |
417 | ut_free(block); |
418 | } else { |
419 | ut_ad(type & MEM_HEAP_BUFFER); |
420 | buf_block_free(buf_block); |
421 | } |
422 | } |
423 | |
424 | /******************************************************************//** |
425 | Frees the free_block field from a memory heap. */ |
426 | void |
427 | mem_heap_free_block_free( |
428 | /*=====================*/ |
429 | mem_heap_t* heap) /*!< in: heap */ |
430 | { |
431 | if (UNIV_LIKELY_NULL(heap->free_block)) { |
432 | |
433 | buf_block_free(static_cast<buf_block_t*>(heap->free_block)); |
434 | |
435 | heap->free_block = NULL; |
436 | } |
437 | } |
438 | |