Drizzled Public API Documentation

mem0mem.cc
1 /*****************************************************************************
2 
3 Copyright (C) 1994, 2010, Innobase Oy. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15 St, Fifth Floor, Boston, MA 02110-1301 USA
16 
17 *****************************************************************************/
18 
19 /********************************************************************/
26 #include "mem0mem.h"
27 
28 #ifdef UNIV_NONINL
29 #include "mem0mem.ic"
30 #endif
31 
32 #include "buf0buf.h"
33 #include "srv0srv.h"
34 #include "mem0dbg.cc"
35 #include <stdarg.h>
36 
37 /*
38  THE MEMORY MANAGEMENT
39  =====================
40 
41 The basic element of the memory management is called a memory
42 heap. A memory heap is conceptually a
43 stack from which memory can be allocated. The stack may grow infinitely.
44 The top element of the stack may be freed, or
45 the whole stack can be freed at one time. The advantage of the
46 memory heap concept is that we can avoid using the malloc and free
47 functions of C which are quite expensive, for example, on the Solaris + GCC
48 system (50 MHz Sparc, 1993) the pair takes 3 microseconds,
49 on Win NT + 100MHz Pentium, 2.5 microseconds.
50 When we use a memory heap,
51 we can allocate larger blocks of memory at a time and thus
52 reduce overhead. Slightly more efficient the method is when we
53 allocate the memory from the index page buffer pool, as we can
54 claim a new page fast. This is called buffer allocation.
55 When we allocate the memory from the dynamic memory of the
56 C environment, that is called dynamic allocation.
57 
58 The default way of operation of the memory heap is the following.
59 First, when the heap is created, an initial block of memory is
60 allocated. In dynamic allocation this may be about 50 bytes.
61 If more space is needed, additional blocks are allocated
62 and they are put into a linked list.
63 After the initial block, each allocated block is twice the size of the
64 previous, until a threshold is attained, after which the sizes
65 of the blocks stay the same. An exception is, of course, the case
66 where the caller requests a memory buffer whose size is
67 bigger than the threshold. In that case a block big enough must
68 be allocated.
69 
70 The heap is physically arranged so that if the current block
71 becomes full, a new block is allocated and always inserted in the
72 chain of blocks as the last block.
73 
74 In the debug version of the memory management, all the allocated
75 heaps are kept in a list (which is implemented as a hash table).
76 Thus we can notice if the caller tries to free an already freed
77 heap. In addition, each buffer given to the caller contains
78 start field at the start and a trailer field at the end of the buffer.
79 
80 The start field has the following content:
81 A. sizeof(ulint) bytes of field length (in the standard byte order)
82 B. sizeof(ulint) bytes of check field (a random number)
83 
84 The trailer field contains:
85 A. sizeof(ulint) bytes of check field (the same random number as at the start)
86 
87 Thus we can notice if something has been copied over the
88 borders of the buffer, which is illegal.
89 The memory in the buffers is initialized to a random byte sequence.
90 After freeing, all the blocks in the heap are set to random bytes
91 to help us discover errors which result from the use of
92 buffers in an already freed heap. */
93 
94 #ifdef MEM_PERIODIC_CHECK
95 
96 ibool mem_block_list_inited;
97 /* List of all mem blocks allocated; protected by the mem_comm_pool mutex */
98 UT_LIST_BASE_NODE_T(mem_block_t) mem_block_list;
99 
100 #endif
101 
102 /**********************************************************************/
105 UNIV_INTERN
106 char*
108 /*============*/
109  mem_heap_t* heap,
110  const char* str)
111 {
112  return(static_cast<char *>(mem_heap_dup(heap, str, strlen(str) + 1)));
113 }
114 
115 /**********************************************************************/
118 UNIV_INTERN
119 void*
121 /*=========*/
122  mem_heap_t* heap,
123  const void* data,
124  ulint len)
125 {
126  return(memcpy(mem_heap_alloc(heap, len), data, len));
127 }
128 
129 /**********************************************************************/
132 UNIV_INTERN
133 char*
135 /*============*/
136  mem_heap_t* heap,
137  const char* s1,
138  const char* s2)
139 {
140  char* s;
141  ulint s1_len = strlen(s1);
142  ulint s2_len = strlen(s2);
143 
144  s = static_cast<char *>(mem_heap_alloc(heap, s1_len + s2_len + 1));
145 
146  memcpy(s, s1, s1_len);
147  memcpy(s + s1_len, s2, s2_len);
148 
149  s[s1_len + s2_len] = '\0';
150 
151  return(s);
152 }
153 
154 
155 /****************************************************************/
158 static
159 ulint
160 mem_heap_printf_low(
161 /*================*/
162  char* buf,
164  const char* format,
165  va_list ap)
166 {
167  ulint len = 0;
168 
169  while (*format) {
170 
171  /* Does this format specifier have the 'l' length modifier. */
172  ibool is_long = FALSE;
173 
174  /* Length of one parameter. */
175  size_t plen;
176 
177  if (*format++ != '%') {
178  /* Non-format character. */
179 
180  len++;
181 
182  if (buf) {
183  *buf++ = *(format - 1);
184  }
185 
186  continue;
187  }
188 
189  if (*format == 'l') {
190  is_long = TRUE;
191  format++;
192  }
193 
194  switch (*format++) {
195  case 's':
196  /* string */
197  {
198  char* s = va_arg(ap, char*);
199 
200  /* "%ls" is a non-sensical format specifier. */
201  ut_a(!is_long);
202 
203  plen = strlen(s);
204  len += plen;
205 
206  if (buf) {
207  memcpy(buf, s, plen);
208  buf += plen;
209  }
210  }
211 
212  break;
213 
214  case 'u':
215  /* unsigned int */
216  {
217  char tmp[32];
218  unsigned long val;
219 
220  /* We only support 'long' values for now. */
221  ut_a(is_long);
222 
223  val = va_arg(ap, unsigned long);
224 
225  plen = sprintf(tmp, "%lu", val);
226  len += plen;
227 
228  if (buf) {
229  memcpy(buf, tmp, plen);
230  buf += plen;
231  }
232  }
233 
234  break;
235 
236  case '%':
237 
238  /* "%l%" is a non-sensical format specifier. */
239  ut_a(!is_long);
240 
241  len++;
242 
243  if (buf) {
244  *buf++ = '%';
245  }
246 
247  break;
248 
249  default:
250  ut_error;
251  }
252  }
253 
254  /* For the NUL character. */
255  len++;
256 
257  if (buf) {
258  *buf = '\0';
259  }
260 
261  return(len);
262 }
263 
264 /****************************************************************/
270 UNIV_INTERN
271 char*
273 /*============*/
274  mem_heap_t* heap,
275  const char* format,
276  ...)
277 {
278  va_list ap;
279  char* str;
280  ulint len;
281 
282  /* Calculate length of string */
283  len = 0;
284  va_start(ap, format);
285  len = mem_heap_printf_low(NULL, format, ap);
286  va_end(ap);
287 
288  /* Now create it for real. */
289  str = static_cast<char *>(mem_heap_alloc(heap, len));
290  va_start(ap, format);
291  mem_heap_printf_low(str, format, ap);
292  va_end(ap);
293 
294  return(str);
295 }
296 
297 /***************************************************************/
301 UNIV_INTERN
303 mem_heap_create_block(
304 /*==================*/
305  mem_heap_t* heap,
307  ulint n,
308  ulint type,
310  const char* file_name,
311  ulint line)
312 {
313 #ifndef UNIV_HOTBACKUP
314  buf_block_t* buf_block = NULL;
315 #endif /* !UNIV_HOTBACKUP */
316  mem_block_t* block;
317  ulint len;
318 
319  ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER)
320  || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH));
321 
322  if (heap && heap->magic_n != MEM_BLOCK_MAGIC_N) {
324  }
325 
326  /* In dynamic allocation, calculate the size: block header + data. */
327  len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
328 
329 #ifndef UNIV_HOTBACKUP
330  if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) {
331 
332  ut_ad(type == MEM_HEAP_DYNAMIC || n <= MEM_MAX_ALLOC_IN_BUF);
333 
334  block = static_cast<mem_block_info_t *>(mem_area_alloc(&len, mem_comm_pool));
335  } else {
336  len = UNIV_PAGE_SIZE;
337 
338  if ((type & MEM_HEAP_BTR_SEARCH) && heap) {
339  /* We cannot allocate the block from the
340  buffer pool, but must get the free block from
341  the heap header free block field */
342 
343  buf_block = static_cast<buf_block_t *>(heap->free_block);
344  heap->free_block = NULL;
345 
346  if (UNIV_UNLIKELY(!buf_block)) {
347 
348  return(NULL);
349  }
350  } else {
351  buf_block = buf_block_alloc(NULL);
352  }
353 
354  block = (mem_block_t*) buf_block->frame;
355  }
356 
357  ut_ad(block);
358  block->buf_block = buf_block;
359  block->free_block = NULL;
360 #else /* !UNIV_HOTBACKUP */
361  len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
362  block = ut_malloc(len);
363  ut_ad(block);
364 #endif /* !UNIV_HOTBACKUP */
365 
366  block->magic_n = MEM_BLOCK_MAGIC_N;
367  ut_strlcpy_rev(block->file_name, file_name, sizeof(block->file_name));
368  block->line = line;
369 
370 #ifdef MEM_PERIODIC_CHECK
371  mutex_enter(&(mem_comm_pool->mutex));
372 
373  if (!mem_block_list_inited) {
374  mem_block_list_inited = TRUE;
375  UT_LIST_INIT(mem_block_list);
376  }
377 
378  UT_LIST_ADD_LAST(mem_block_list, mem_block_list, block);
379 
380  mutex_exit(&(mem_comm_pool->mutex));
381 #endif
382  mem_block_set_len(block, len);
383  mem_block_set_type(block, type);
384  mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE);
385  mem_block_set_start(block, MEM_BLOCK_HEADER_SIZE);
386 
387  if (UNIV_UNLIKELY(heap == NULL)) {
388  /* This is the first block of the heap. The field
389  total_size should be initialized here */
390  block->total_size = len;
391  } else {
392  /* Not the first allocation for the heap. This block's
393  total_length field should be set to undefined. */
394  ut_d(block->total_size = ULINT_UNDEFINED);
395  UNIV_MEM_INVALID(&block->total_size,
396  sizeof block->total_size);
397 
398  heap->total_size += len;
399  }
400 
401  ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len);
402 
403  return(block);
404 }
405 
406 /***************************************************************/
410 UNIV_INTERN
412 mem_heap_add_block(
413 /*===============*/
414  mem_heap_t* heap,
415  ulint n)
416 {
417  mem_block_t* block;
418  mem_block_t* new_block;
419  ulint new_size;
420 
421  ut_ad(mem_heap_check(heap));
422 
423  block = UT_LIST_GET_LAST(heap->base);
424 
425  /* We have to allocate a new block. The size is always at least
426  doubled until the standard size is reached. After that the size
427  stays the same, except in cases where the caller needs more space. */
428 
429  new_size = 2 * mem_block_get_len(block);
430 
431  if (heap->type != MEM_HEAP_DYNAMIC) {
432  /* From the buffer pool we allocate buffer frames */
433  ut_a(n <= MEM_MAX_ALLOC_IN_BUF);
434 
435  if (new_size > MEM_MAX_ALLOC_IN_BUF) {
436  new_size = MEM_MAX_ALLOC_IN_BUF;
437  }
438  } else if (new_size > MEM_BLOCK_STANDARD_SIZE) {
439 
440  new_size = MEM_BLOCK_STANDARD_SIZE;
441  }
442 
443  if (new_size < n) {
444  new_size = n;
445  }
446 
447  new_block = mem_heap_create_block(heap, new_size, heap->type,
448  heap->file_name, heap->line);
449  if (new_block == NULL) {
450 
451  return(NULL);
452  }
453 
454  /* Add the new block as the last block */
455 
456  UT_LIST_INSERT_AFTER(list, heap->base, block, new_block);
457 
458  return(new_block);
459 }
460 
461 /******************************************************************/
463 UNIV_INTERN
464 void
465 mem_heap_block_free(
466 /*================*/
467  mem_heap_t* heap,
468  mem_block_t* block)
469 {
470  ulint type;
471  ulint len;
472 #ifndef UNIV_HOTBACKUP
473  buf_block_t* buf_block = static_cast<buf_block_t *>(block->buf_block);
474 #endif /* !UNIV_HOTBACKUP */
475 
476  if (block->magic_n != MEM_BLOCK_MAGIC_N) {
477  mem_analyze_corruption(block);
478  }
479 
480  UT_LIST_REMOVE(list, heap->base, block);
481 
482 #ifdef MEM_PERIODIC_CHECK
483  mutex_enter(&(mem_comm_pool->mutex));
484 
485  UT_LIST_REMOVE(mem_block_list, mem_block_list, block);
486 
487  mutex_exit(&(mem_comm_pool->mutex));
488 #endif
489 
490  ut_ad(heap->total_size >= block->len);
491  heap->total_size -= block->len;
492 
493  type = heap->type;
494  len = block->len;
495  block->magic_n = MEM_FREED_BLOCK_MAGIC_N;
496 
497 #ifndef UNIV_HOTBACKUP
498 #ifdef UNIV_MEM_DEBUG
499  /* In the debug version we set the memory to a random combination
500  of hex 0xDE and 0xAD. */
501 
502  mem_erase_buf((byte*)block, len);
503 #else /* UNIV_MEM_DEBUG */
504  UNIV_MEM_ASSERT_AND_FREE(block, len);
505 #endif /* UNIV_MEM_DEBUG */
506 
507  if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) {
508 
509  ut_ad(!buf_block);
510  mem_area_free(block, mem_comm_pool);
511  } else {
512  ut_ad(type & MEM_HEAP_BUFFER);
513 
514  buf_block_free(buf_block);
515  }
516 #else /* !UNIV_HOTBACKUP */
517 #ifdef UNIV_MEM_DEBUG
518  /* In the debug version we set the memory to a random
519  combination of hex 0xDE and 0xAD. */
520 
521  mem_erase_buf((byte*)block, len);
522 #else /* UNIV_MEM_DEBUG */
523  UNIV_MEM_ASSERT_AND_FREE(block, len);
524 #endif /* UNIV_MEM_DEBUG */
525  ut_free(block);
526 #endif /* !UNIV_HOTBACKUP */
527 }
528 
529 #ifndef UNIV_HOTBACKUP
530 /******************************************************************/
532 UNIV_INTERN
533 void
534 mem_heap_free_block_free(
535 /*=====================*/
536  mem_heap_t* heap)
537 {
538  if (UNIV_LIKELY_NULL(heap->free_block)) {
539 
540  buf_block_free(static_cast<buf_block_t *>(heap->free_block));
541 
542  heap->free_block = NULL;
543  }
544 }
545 #endif /* !UNIV_HOTBACKUP */
546 
547 #ifdef MEM_PERIODIC_CHECK
548 /******************************************************************/
551 UNIV_INTERN
552 void
553 mem_validate_all_blocks(void)
554 /*=========================*/
555 {
556  mem_block_t* block;
557 
558  mutex_enter(&(mem_comm_pool->mutex));
559 
560  block = UT_LIST_GET_FIRST(mem_block_list);
561 
562  while (block) {
563  if (block->magic_n != MEM_BLOCK_MAGIC_N) {
564  mem_analyze_corruption(block);
565  }
566 
567  block = UT_LIST_GET_NEXT(mem_block_list, block);
568  }
569 
570  mutex_exit(&(mem_comm_pool->mutex));
571 }
572 #endif