summaryrefslogtreecommitdiff
path: root/storage/innobase/mem
diff options
context:
space:
mode:
authorGuilhem Bichot <guilhem@mysql.com>2009-08-07 12:16:00 +0200
committerGuilhem Bichot <guilhem@mysql.com>2009-08-07 12:16:00 +0200
commit7ceb29ff17047a268d8e8670478f6e1669939904 (patch)
tree65b42f5cb11f29ea5b4414ff075ccafd48569ad6 /storage/innobase/mem
parent7fa1449eac29401b3d1f3fb4980149e9e562a705 (diff)
downloadmariadb-git-7ceb29ff17047a268d8e8670478f6e1669939904.tar.gz
Renamed storage/innodb_plugin to storage/innobase, so that 1) it's the same
layout as we always had in trees containing only the builtin 2) win\configure.js WITH_INNOBASE_STORAGE_ENGINE still works. storage/innobase/CMakeLists.txt: fix to new directory name (and like 5.1) storage/innobase/Makefile.am: fix to new directory name (and like 5.1) storage/innobase/handler/ha_innodb.cc: fix to new directory name (and like 5.1) storage/innobase/plug.in: fix to new directory name (and like 5.1)
Diffstat (limited to 'storage/innobase/mem')
-rw-r--r--storage/innobase/mem/mem0dbg.c1026
-rw-r--r--storage/innobase/mem/mem0mem.c545
-rw-r--r--storage/innobase/mem/mem0pool.c705
3 files changed, 2276 insertions, 0 deletions
diff --git a/storage/innobase/mem/mem0dbg.c b/storage/innobase/mem/mem0dbg.c
new file mode 100644
index 00000000000..a20eb2ad7d2
--- /dev/null
+++ b/storage/innobase/mem/mem0dbg.c
@@ -0,0 +1,1026 @@
+/*****************************************************************************
+
+Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+*****************************************************************************/
+
+/********************************************************************//**
+@file mem/mem0dbg.c
+The memory management: the debug code. This is not a compilation module,
+but is included in mem0mem.* !
+
+Created 6/9/1994 Heikki Tuuri
+*************************************************************************/
+
+#ifdef UNIV_MEM_DEBUG
+# ifndef UNIV_HOTBACKUP
+/* The mutex which protects in the debug version the hash table
+containing the list of live memory heaps, and also the global
+variables below. */
+UNIV_INTERN mutex_t mem_hash_mutex;
+# endif /* !UNIV_HOTBACKUP */
+
+/* The following variables contain information about the
+extent of memory allocations. Only used in the debug version.
+Protected by mem_hash_mutex above. */
+
+static ulint mem_n_created_heaps = 0;
+static ulint mem_n_allocations = 0;
+static ulint mem_total_allocated_memory = 0;
+UNIV_INTERN ulint mem_current_allocated_memory = 0;
+static ulint mem_max_allocated_memory = 0;
+# ifndef UNIV_HOTBACKUP
+static ulint mem_last_print_info = 0;
+static ibool mem_hash_initialized = FALSE;
+# endif /* !UNIV_HOTBACKUP */
+
+/* Size of the hash table for memory management tracking */
+#define MEM_HASH_SIZE 997
+
+/* The node of the list containing currently allocated memory heaps */
+
+typedef struct mem_hash_node_struct mem_hash_node_t;
+struct mem_hash_node_struct {
+ UT_LIST_NODE_T(mem_hash_node_t)
+ list; /*!< hash list node */
+ mem_heap_t* heap; /*!< memory heap */
+ const char* file_name;/* file where heap was created*/
+ ulint line; /*!< file line of creation */
+ ulint nth_heap;/* this is the nth heap created */
+ UT_LIST_NODE_T(mem_hash_node_t)
+ all_list;/* list of all created heaps */
+};
+
+typedef UT_LIST_BASE_NODE_T(mem_hash_node_t) mem_hash_cell_t;
+
+/* The hash table of allocated heaps */
+static mem_hash_cell_t mem_hash_table[MEM_HASH_SIZE];
+
+/* The base node of the list of all allocated heaps */
+static mem_hash_cell_t mem_all_list_base;
+
+
+
+UNIV_INLINE
+mem_hash_cell_t*
+mem_hash_get_nth_cell(ulint i);
+
+/* Accessor function for the hash table. Returns a pointer to the
+table cell. */
+UNIV_INLINE
+mem_hash_cell_t*
+mem_hash_get_nth_cell(ulint i)
+{
+ ut_a(i < MEM_HASH_SIZE);
+
+ return(&(mem_hash_table[i]));
+}
+
+/* Accessor functions for a memory field in the debug version */
+UNIV_INTERN
+void
+mem_field_header_set_len(byte* field, ulint len)
+{
+ mach_write_to_4(field - 2 * sizeof(ulint), len);
+}
+
+UNIV_INTERN
+ulint
+mem_field_header_get_len(byte* field)
+{
+ return(mach_read_from_4(field - 2 * sizeof(ulint)));
+}
+
+UNIV_INTERN
+void
+mem_field_header_set_check(byte* field, ulint check)
+{
+ mach_write_to_4(field - sizeof(ulint), check);
+}
+
+UNIV_INTERN
+ulint
+mem_field_header_get_check(byte* field)
+{
+ return(mach_read_from_4(field - sizeof(ulint)));
+}
+
+UNIV_INTERN
+void
+mem_field_trailer_set_check(byte* field, ulint check)
+{
+ mach_write_to_4(field + mem_field_header_get_len(field), check);
+}
+
+UNIV_INTERN
+ulint
+mem_field_trailer_get_check(byte* field)
+{
+ return(mach_read_from_4(field
+ + mem_field_header_get_len(field)));
+}
+#endif /* UNIV_MEM_DEBUG */
+
+#ifndef UNIV_HOTBACKUP
+/******************************************************************//**
+Initializes the memory system. */
+UNIV_INTERN
+void
+mem_init(
+/*=====*/
+ ulint size) /*!< in: common pool size in bytes */
+{
+#ifdef UNIV_MEM_DEBUG
+
+ ulint i;
+
+ /* Initialize the hash table */
+ ut_a(FALSE == mem_hash_initialized);
+
+ mutex_create(&mem_hash_mutex, SYNC_MEM_HASH);
+
+ for (i = 0; i < MEM_HASH_SIZE; i++) {
+ UT_LIST_INIT(*mem_hash_get_nth_cell(i));
+ }
+
+ UT_LIST_INIT(mem_all_list_base);
+
+ mem_hash_initialized = TRUE;
+#endif
+
+ if (UNIV_LIKELY(srv_use_sys_malloc)) {
+ /* When innodb_use_sys_malloc is set, the
+ mem_comm_pool won't be used for any allocations. We
+ create a dummy mem_comm_pool, because some statistics
+ and debugging code relies on it being initialized. */
+ size = 1;
+ }
+
+ mem_comm_pool = mem_pool_create(size);
+}
+#endif /* !UNIV_HOTBACKUP */
+
+#ifdef UNIV_MEM_DEBUG
+/******************************************************************//**
+Initializes an allocated memory field in the debug version. */
+UNIV_INTERN
+void
+mem_field_init(
+/*===========*/
+ byte* buf, /*!< in: memory field */
+ ulint n) /*!< in: how many bytes the user requested */
+{
+ ulint rnd;
+ byte* usr_buf;
+
+ usr_buf = buf + MEM_FIELD_HEADER_SIZE;
+
+ /* In the debug version write the length field and the
+ check fields to the start and the end of the allocated storage.
+ The field header consists of a length field and
+ a random number field, in this order. The field trailer contains
+ the same random number as a check field. */
+
+ mem_field_header_set_len(usr_buf, n);
+
+ rnd = ut_rnd_gen_ulint();
+
+ mem_field_header_set_check(usr_buf, rnd);
+ mem_field_trailer_set_check(usr_buf, rnd);
+
+ /* Update the memory allocation information */
+
+ mutex_enter(&mem_hash_mutex);
+
+ mem_total_allocated_memory += n;
+ mem_current_allocated_memory += n;
+ mem_n_allocations++;
+
+ if (mem_current_allocated_memory > mem_max_allocated_memory) {
+ mem_max_allocated_memory = mem_current_allocated_memory;
+ }
+
+ mutex_exit(&mem_hash_mutex);
+
+ /* In the debug version set the buffer to a random
+ combination of 0xBA and 0xBE */
+
+ mem_init_buf(usr_buf, n);
+}
+
+/******************************************************************//**
+Erases an allocated memory field in the debug version. */
+UNIV_INTERN
+void
+mem_field_erase(
+/*============*/
+ byte* buf, /*!< in: memory field */
+ ulint n __attribute__((unused)))
+ /*!< in: how many bytes the user requested */
+{
+ byte* usr_buf;
+
+ usr_buf = buf + MEM_FIELD_HEADER_SIZE;
+
+ mutex_enter(&mem_hash_mutex);
+ mem_current_allocated_memory -= n;
+ mutex_exit(&mem_hash_mutex);
+
+ /* Check that the field lengths agree */
+ ut_ad(n == (ulint)mem_field_header_get_len(usr_buf));
+
+ /* In the debug version, set the freed space to a random
+ combination of 0xDE and 0xAD */
+
+ mem_erase_buf(buf, MEM_SPACE_NEEDED(n));
+}
+
+/***************************************************************//**
+Initializes a buffer to a random combination of hex BA and BE.
+Used to initialize allocated memory. */
+UNIV_INTERN
+void
+mem_init_buf(
+/*=========*/
+ byte* buf, /*!< in: pointer to buffer */
+ ulint n) /*!< in: length of buffer */
+{
+ byte* ptr;
+
+ UNIV_MEM_ASSERT_W(buf, n);
+
+ for (ptr = buf; ptr < buf + n; ptr++) {
+
+ if (ut_rnd_gen_ibool()) {
+ *ptr = 0xBA;
+ } else {
+ *ptr = 0xBE;
+ }
+ }
+
+ UNIV_MEM_INVALID(buf, n);
+}
+
+/***************************************************************//**
+Initializes a buffer to a random combination of hex DE and AD.
+Used to erase freed memory. */
+UNIV_INTERN
+void
+mem_erase_buf(
+/*==========*/
+ byte* buf, /*!< in: pointer to buffer */
+ ulint n) /*!< in: length of buffer */
+{
+ byte* ptr;
+
+ UNIV_MEM_ASSERT_W(buf, n);
+
+ for (ptr = buf; ptr < buf + n; ptr++) {
+ if (ut_rnd_gen_ibool()) {
+ *ptr = 0xDE;
+ } else {
+ *ptr = 0xAD;
+ }
+ }
+
+ UNIV_MEM_FREE(buf, n);
+}
+
+/***************************************************************//**
+Inserts a created memory heap to the hash table of current allocated
+memory heaps. */
+UNIV_INTERN
+void
+mem_hash_insert(
+/*============*/
+ mem_heap_t* heap, /*!< in: the created heap */
+ const char* file_name, /*!< in: file name of creation */
+ ulint line) /*!< in: line where created */
+{
+ mem_hash_node_t* new_node;
+ ulint cell_no ;
+
+ ut_ad(mem_heap_check(heap));
+
+ mutex_enter(&mem_hash_mutex);
+
+ cell_no = ut_hash_ulint((ulint)heap, MEM_HASH_SIZE);
+
+ /* Allocate a new node to the list */
+ new_node = ut_malloc(sizeof(mem_hash_node_t));
+
+ new_node->heap = heap;
+ new_node->file_name = file_name;
+ new_node->line = line;
+ new_node->nth_heap = mem_n_created_heaps;
+
+ /* Insert into lists */
+ UT_LIST_ADD_FIRST(list, *mem_hash_get_nth_cell(cell_no), new_node);
+
+ UT_LIST_ADD_LAST(all_list, mem_all_list_base, new_node);
+
+ mem_n_created_heaps++;
+
+ mutex_exit(&mem_hash_mutex);
+}
+
+/***************************************************************//**
+Removes a memory heap (which is going to be freed by the caller)
+from the list of live memory heaps. Returns the size of the heap
+in terms of how much memory in bytes was allocated for the user of
+the heap (not the total space occupied by the heap).
+Also validates the heap.
+NOTE: This function does not free the storage occupied by the
+heap itself, only the node in the list of heaps. */
+UNIV_INTERN
+void
+mem_hash_remove(
+/*============*/
+ mem_heap_t* heap, /*!< in: the heap to be freed */
+ const char* file_name, /*!< in: file name of freeing */
+ ulint line) /*!< in: line where freed */
+{
+ mem_hash_node_t* node;
+ ulint cell_no;
+ ibool error;
+ ulint size;
+
+ ut_ad(mem_heap_check(heap));
+
+ mutex_enter(&mem_hash_mutex);
+
+ cell_no = ut_hash_ulint((ulint)heap, MEM_HASH_SIZE);
+
+ /* Look for the heap in the hash table list */
+ node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(cell_no));
+
+ while (node != NULL) {
+ if (node->heap == heap) {
+
+ break;
+ }
+
+ node = UT_LIST_GET_NEXT(list, node);
+ }
+
+ if (node == NULL) {
+ fprintf(stderr,
+ "Memory heap or buffer freed in %s line %lu"
+ " did not exist.\n",
+ file_name, (ulong) line);
+ ut_error;
+ }
+
+ /* Remove from lists */
+ UT_LIST_REMOVE(list, *mem_hash_get_nth_cell(cell_no), node);
+
+ UT_LIST_REMOVE(all_list, mem_all_list_base, node);
+
+ /* Validate the heap which will be freed */
+ mem_heap_validate_or_print(node->heap, NULL, FALSE, &error, &size,
+ NULL, NULL);
+ if (error) {
+ fprintf(stderr,
+ "Inconsistency in memory heap or"
+ " buffer n:o %lu created\n"
+ "in %s line %lu and tried to free in %s line %lu.\n"
+ "Hex dump of 400 bytes around memory heap"
+ " first block start:\n",
+ node->nth_heap, node->file_name, (ulong) node->line,
+ file_name, (ulong) line);
+ ut_print_buf(stderr, (byte*)node->heap - 200, 400);
+ fputs("\nDump of the mem heap:\n", stderr);
+ mem_heap_validate_or_print(node->heap, NULL, TRUE, &error,
+ &size, NULL, NULL);
+ ut_error;
+ }
+
+ /* Free the memory occupied by the node struct */
+ ut_free(node);
+
+ mem_current_allocated_memory -= size;
+
+ mutex_exit(&mem_hash_mutex);
+}
+#endif /* UNIV_MEM_DEBUG */
+
+#if defined UNIV_MEM_DEBUG || defined UNIV_DEBUG
+/***************************************************************//**
+Checks a memory heap for consistency and prints the contents if requested.
+Outputs the sum of sizes of buffers given to the user (only in
+the debug version), the physical size of the heap and the number of
+blocks in the heap. In case of error returns 0 as sizes and number
+of blocks. */
+UNIV_INTERN
+void
+mem_heap_validate_or_print(
+/*=======================*/
+ mem_heap_t* heap, /*!< in: memory heap */
+ byte* top __attribute__((unused)),
+ /*!< in: calculate and validate only until
+ this top pointer in the heap is reached,
+ if this pointer is NULL, ignored */
+ ibool print, /*!< in: if TRUE, prints the contents
+ of the heap; works only in
+ the debug version */
+ ibool* error, /*!< out: TRUE if error */
+ ulint* us_size,/*!< out: allocated memory
+ (for the user) in the heap,
+ if a NULL pointer is passed as this
+ argument, it is ignored; in the
+ non-debug version this is always -1 */
+ ulint* ph_size,/*!< out: physical size of the heap,
+ if a NULL pointer is passed as this
+ argument, it is ignored */
+ ulint* n_blocks) /*!< out: number of blocks in the heap,
+ if a NULL pointer is passed as this
+ argument, it is ignored */
+{
+ mem_block_t* block;
+ ulint total_len = 0;
+ ulint block_count = 0;
+ ulint phys_len = 0;
+#ifdef UNIV_MEM_DEBUG
+ ulint len;
+ byte* field;
+ byte* user_field;
+ ulint check_field;
+#endif
+
+ /* Pessimistically, we set the parameters to error values */
+ if (us_size != NULL) {
+ *us_size = 0;
+ }
+ if (ph_size != NULL) {
+ *ph_size = 0;
+ }
+ if (n_blocks != NULL) {
+ *n_blocks = 0;
+ }
+ *error = TRUE;
+
+ block = heap;
+
+ if (block->magic_n != MEM_BLOCK_MAGIC_N) {
+ return;
+ }
+
+ if (print) {
+ fputs("Memory heap:", stderr);
+ }
+
+ while (block != NULL) {
+ phys_len += mem_block_get_len(block);
+
+ if ((block->type == MEM_HEAP_BUFFER)
+ && (mem_block_get_len(block) > UNIV_PAGE_SIZE)) {
+
+ fprintf(stderr,
+ "InnoDB: Error: mem block %p"
+ " length %lu > UNIV_PAGE_SIZE\n",
+ (void*) block,
+ (ulong) mem_block_get_len(block));
+ /* error */
+
+ return;
+ }
+
+#ifdef UNIV_MEM_DEBUG
+ /* We can trace the fields of the block only in the debug
+ version */
+ if (print) {
+ fprintf(stderr, " Block %ld:", block_count);
+ }
+
+ field = (byte*)block + mem_block_get_start(block);
+
+ if (top && (field == top)) {
+
+ goto completed;
+ }
+
+ while (field < (byte*)block + mem_block_get_free(block)) {
+
+ /* Calculate the pointer to the storage
+ which was given to the user */
+
+ user_field = field + MEM_FIELD_HEADER_SIZE;
+
+ len = mem_field_header_get_len(user_field);
+
+ if (print) {
+ ut_print_buf(stderr, user_field, len);
+ putc('\n', stderr);
+ }
+
+ total_len += len;
+ check_field = mem_field_header_get_check(user_field);
+
+ if (check_field
+ != mem_field_trailer_get_check(user_field)) {
+ /* error */
+
+ fprintf(stderr,
+ "InnoDB: Error: block %lx mem"
+ " field %lx len %lu\n"
+ "InnoDB: header check field is"
+ " %lx but trailer %lx\n",
+ (ulint)block,
+ (ulint)field, len, check_field,
+ mem_field_trailer_get_check(
+ user_field));
+
+ return;
+ }
+
+ /* Move to next field */
+ field = field + MEM_SPACE_NEEDED(len);
+
+ if (top && (field == top)) {
+
+ goto completed;
+ }
+
+ }
+
+ /* At the end check that we have arrived to the first free
+ position */
+
+ if (field != (byte*)block + mem_block_get_free(block)) {
+ /* error */
+
+ fprintf(stderr,
+ "InnoDB: Error: block %lx end of"
+ " mem fields %lx\n"
+ "InnoDB: but block free at %lx\n",
+ (ulint)block, (ulint)field,
+ (ulint)((byte*)block
+ + mem_block_get_free(block)));
+
+ return;
+ }
+
+#endif
+
+ block = UT_LIST_GET_NEXT(list, block);
+ block_count++;
+ }
+#ifdef UNIV_MEM_DEBUG
+completed:
+#endif
+ if (us_size != NULL) {
+ *us_size = total_len;
+ }
+ if (ph_size != NULL) {
+ *ph_size = phys_len;
+ }
+ if (n_blocks != NULL) {
+ *n_blocks = block_count;
+ }
+ *error = FALSE;
+}
+
+/**************************************************************//**
+Prints the contents of a memory heap. */
+static
+void
+mem_heap_print(
+/*===========*/
+ mem_heap_t* heap) /*!< in: memory heap */
+{
+ ibool error;
+ ulint us_size;
+ ulint phys_size;
+ ulint n_blocks;
+
+ ut_ad(mem_heap_check(heap));
+
+ mem_heap_validate_or_print(heap, NULL, TRUE, &error,
+ &us_size, &phys_size, &n_blocks);
+ fprintf(stderr,
+ "\nheap type: %lu; size: user size %lu;"
+ " physical size %lu; blocks %lu.\n",
+ (ulong) heap->type, (ulong) us_size,
+ (ulong) phys_size, (ulong) n_blocks);
+ ut_a(!error);
+}
+
+/**************************************************************//**
+Validates the contents of a memory heap.
+@return TRUE if ok */
+UNIV_INTERN
+ibool
+mem_heap_validate(
+/*==============*/
+ mem_heap_t* heap) /*!< in: memory heap */
+{
+ ibool error;
+ ulint us_size;
+ ulint phys_size;
+ ulint n_blocks;
+
+ ut_ad(mem_heap_check(heap));
+
+ mem_heap_validate_or_print(heap, NULL, FALSE, &error, &us_size,
+ &phys_size, &n_blocks);
+ if (error) {
+ mem_heap_print(heap);
+ }
+
+ ut_a(!error);
+
+ return(TRUE);
+}
+#endif /* UNIV_MEM_DEBUG || UNIV_DEBUG */
+
+#ifdef UNIV_DEBUG
+/**************************************************************//**
+Checks that an object is a memory heap (or a block of it).
+@return TRUE if ok */
+UNIV_INTERN
+ibool
+mem_heap_check(
+/*===========*/
+ mem_heap_t* heap) /*!< in: memory heap */
+{
+ ut_a(heap->magic_n == MEM_BLOCK_MAGIC_N);
+
+ return(TRUE);
+}
+#endif /* UNIV_DEBUG */
+
+#ifdef UNIV_MEM_DEBUG
+/*****************************************************************//**
+TRUE if no memory is currently allocated.
+@return TRUE if no heaps exist */
+UNIV_INTERN
+ibool
+mem_all_freed(void)
+/*===============*/
+{
+ mem_hash_node_t* node;
+ ulint heap_count = 0;
+ ulint i;
+
+ mem_validate();
+
+ mutex_enter(&mem_hash_mutex);
+
+ for (i = 0; i < MEM_HASH_SIZE; i++) {
+
+ node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(i));
+ while (node != NULL) {
+ heap_count++;
+ node = UT_LIST_GET_NEXT(list, node);
+ }
+ }
+
+ mutex_exit(&mem_hash_mutex);
+
+ if (heap_count == 0) {
+# ifndef UNIV_HOTBACKUP
+ ut_a(mem_pool_get_reserved(mem_comm_pool) == 0);
+# endif /* !UNIV_HOTBACKUP */
+
+ return(TRUE);
+ } else {
+ return(FALSE);
+ }
+}
+
+/*****************************************************************//**
+Validates the dynamic memory allocation system.
+@return TRUE if error */
+UNIV_INTERN
+ibool
+mem_validate_no_assert(void)
+/*========================*/
+{
+ mem_hash_node_t* node;
+ ulint n_heaps = 0;
+ ulint allocated_mem;
+ ulint ph_size;
+ ulint total_allocated_mem = 0;
+ ibool error = FALSE;
+ ulint n_blocks;
+ ulint i;
+
+# ifndef UNIV_HOTBACKUP
+ mem_pool_validate(mem_comm_pool);
+# endif /* !UNIV_HOTBACKUP */
+
+ mutex_enter(&mem_hash_mutex);
+
+ for (i = 0; i < MEM_HASH_SIZE; i++) {
+
+ node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(i));
+
+ while (node != NULL) {
+ n_heaps++;
+
+ mem_heap_validate_or_print(node->heap, NULL,
+ FALSE, &error,
+ &allocated_mem,
+ &ph_size, &n_blocks);
+
+ if (error) {
+ fprintf(stderr,
+ "\nERROR!!!!!!!!!!!!!!!!!!!"
+ "!!!!!!!!!!!!!!!!!!!!!!!\n\n"
+ "Inconsistency in memory heap"
+ " or buffer created\n"
+ "in %s line %lu.\n",
+ node->file_name, node->line);
+
+ mutex_exit(&mem_hash_mutex);
+
+ return(TRUE);
+ }
+
+ total_allocated_mem += allocated_mem;
+ node = UT_LIST_GET_NEXT(list, node);
+ }
+ }
+
+ if ((n_heaps == 0) && (mem_current_allocated_memory != 0)) {
+ error = TRUE;
+ }
+
+ if (mem_total_allocated_memory < mem_current_allocated_memory) {
+ error = TRUE;
+ }
+
+ if (mem_max_allocated_memory > mem_total_allocated_memory) {
+ error = TRUE;
+ }
+
+ if (mem_n_created_heaps < n_heaps) {
+ error = TRUE;
+ }
+
+ mutex_exit(&mem_hash_mutex);
+
+ return(error);
+}
+
+/************************************************************//**
+Validates the dynamic memory
+@return TRUE if ok */
+UNIV_INTERN
+ibool
+mem_validate(void)
+/*==============*/
+{
+ ut_a(!mem_validate_no_assert());
+
+ return(TRUE);
+}
+#endif /* UNIV_MEM_DEBUG */
+
+/************************************************************//**
+Tries to find neigboring memory allocation blocks and dumps to stderr
+the neighborhood of a given pointer. */
+UNIV_INTERN
+void
+mem_analyze_corruption(
+/*===================*/
+ void* ptr) /*!< in: pointer to place of possible corruption */
+{
+ byte* p;
+ ulint i;
+ ulint dist;
+
+ fputs("InnoDB: Apparent memory corruption: mem dump ", stderr);
+ ut_print_buf(stderr, (byte*)ptr - 250, 500);
+
+ fputs("\nInnoDB: Scanning backward trying to find"
+ " previous allocated mem blocks\n", stderr);
+
+ p = (byte*)ptr;
+ dist = 0;
+
+ for (i = 0; i < 10; i++) {
+ for (;;) {
+ if (((ulint)p) % 4 == 0) {
+
+ if (*((ulint*)p) == MEM_BLOCK_MAGIC_N) {
+ fprintf(stderr,
+ "Mem block at - %lu,"
+ " file %s, line %lu\n",
+ (ulong) dist,
+ (p + sizeof(ulint)),
+ (ulong)
+ (*(ulint*)(p + 8
+ + sizeof(ulint))));
+
+ break;
+ }
+
+ if (*((ulint*)p) == MEM_FREED_BLOCK_MAGIC_N) {
+ fprintf(stderr,
+ "Freed mem block at - %lu,"
+ " file %s, line %lu\n",
+ (ulong) dist,
+ (p + sizeof(ulint)),
+ (ulong)
+ (*(ulint*)(p + 8
+ + sizeof(ulint))));
+
+ break;
+ }
+ }
+
+ p--;
+ dist++;
+ }
+
+ p--;
+ dist++;
+ }
+
+ fprintf(stderr,
+ "InnoDB: Scanning forward trying to find next"
+ " allocated mem blocks\n");
+
+ p = (byte*)ptr;
+ dist = 0;
+
+ for (i = 0; i < 10; i++) {
+ for (;;) {
+ if (((ulint)p) % 4 == 0) {
+
+ if (*((ulint*)p) == MEM_BLOCK_MAGIC_N) {
+ fprintf(stderr,
+ "Mem block at + %lu, file %s,"
+ " line %lu\n",
+ (ulong) dist,
+ (p + sizeof(ulint)),
+ (ulong)
+ (*(ulint*)(p + 8
+ + sizeof(ulint))));
+
+ break;
+ }
+
+ if (*((ulint*)p) == MEM_FREED_BLOCK_MAGIC_N) {
+ fprintf(stderr,
+ "Freed mem block at + %lu,"
+ " file %s, line %lu\n",
+ (ulong) dist,
+ (p + sizeof(ulint)),
+ (ulong)
+ (*(ulint*)(p + 8
+ + sizeof(ulint))));
+
+ break;
+ }
+ }
+
+ p++;
+ dist++;
+ }
+
+ p++;
+ dist++;
+ }
+}
+
+#ifndef UNIV_HOTBACKUP
+/*****************************************************************//**
+Prints information of dynamic memory usage and currently allocated
+memory heaps or buffers. Can only be used in the debug version. */
+static
+void
+mem_print_info_low(
+/*===============*/
+ ibool print_all) /*!< in: if TRUE, all heaps are printed,
+ else only the heaps allocated after the
+ previous call of this function */
+{
+#ifdef UNIV_MEM_DEBUG
+ mem_hash_node_t* node;
+ ulint n_heaps = 0;
+ ulint allocated_mem;
+ ulint ph_size;
+ ulint total_allocated_mem = 0;
+ ibool error;
+ ulint n_blocks;
+#endif
+ FILE* outfile;
+
+ /* outfile = fopen("ibdebug", "a"); */
+
+ outfile = stdout;
+
+ fprintf(outfile, "\n");
+ fprintf(outfile,
+ "________________________________________________________\n");
+ fprintf(outfile, "MEMORY ALLOCATION INFORMATION\n\n");
+
+#ifndef UNIV_MEM_DEBUG
+
+ UT_NOT_USED(print_all);
+
+ mem_pool_print_info(outfile, mem_comm_pool);
+
+ fprintf(outfile,
+ "Sorry, non-debug version cannot give more memory info\n");
+
+ /* fclose(outfile); */
+
+ return;
+#else
+ mutex_enter(&mem_hash_mutex);
+
+ fprintf(outfile, "LIST OF CREATED HEAPS AND ALLOCATED BUFFERS: \n\n");
+
+ if (!print_all) {
+ fprintf(outfile, "AFTER THE LAST PRINT INFO\n");
+ }
+
+ node = UT_LIST_GET_FIRST(mem_all_list_base);
+
+ while (node != NULL) {
+ n_heaps++;
+
+ if (!print_all && node->nth_heap < mem_last_print_info) {
+
+ goto next_heap;
+ }
+
+ mem_heap_validate_or_print(node->heap, NULL,
+ FALSE, &error, &allocated_mem,
+ &ph_size, &n_blocks);
+ total_allocated_mem += allocated_mem;
+
+ fprintf(outfile,
+ "%lu: file %s line %lu of size %lu phys.size %lu"
+ " with %lu blocks, type %lu\n",
+ node->nth_heap, node->file_name, node->line,
+ allocated_mem, ph_size, n_blocks,
+ (node->heap)->type);
+next_heap:
+ node = UT_LIST_GET_NEXT(all_list, node);
+ }
+
+ fprintf(outfile, "\n");
+
+ fprintf(outfile, "Current allocated memory : %lu\n",
+ mem_current_allocated_memory);
+ fprintf(outfile, "Current allocated heaps and buffers : %lu\n",
+ n_heaps);
+ fprintf(outfile, "Cumulative allocated memory : %lu\n",
+ mem_total_allocated_memory);
+ fprintf(outfile, "Maximum allocated memory : %lu\n",
+ mem_max_allocated_memory);
+ fprintf(outfile, "Cumulative created heaps and buffers : %lu\n",
+ mem_n_created_heaps);
+ fprintf(outfile, "Cumulative number of allocations : %lu\n",
+ mem_n_allocations);
+
+ mem_last_print_info = mem_n_created_heaps;
+
+ mutex_exit(&mem_hash_mutex);
+
+ mem_pool_print_info(outfile, mem_comm_pool);
+
+ /* mem_validate(); */
+
+ /* fclose(outfile); */
+#endif
+}
+
+/*****************************************************************//**
+Prints information of dynamic memory usage and currently allocated memory
+heaps or buffers. Can only be used in the debug version. */
+UNIV_INTERN
+void
+mem_print_info(void)
+/*================*/
+{
+ mem_print_info_low(TRUE);
+}
+
+/*****************************************************************//**
+Prints information of dynamic memory usage and currently allocated memory
+heaps or buffers since the last ..._print_info or..._print_new_info. */
+UNIV_INTERN
+void
+mem_print_new_info(void)
+/*====================*/
+{
+ mem_print_info_low(FALSE);
+}
+#endif /* !UNIV_HOTBACKUP */
diff --git a/storage/innobase/mem/mem0mem.c b/storage/innobase/mem/mem0mem.c
new file mode 100644
index 00000000000..e0dc8716f13
--- /dev/null
+++ b/storage/innobase/mem/mem0mem.c
@@ -0,0 +1,545 @@
+/*****************************************************************************
+
+Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+*****************************************************************************/
+
+/********************************************************************//**
+@file mem/mem0mem.c
+The memory management
+
+Created 6/9/1994 Heikki Tuuri
+*************************************************************************/
+
+#include "mem0mem.h"
+#ifdef UNIV_NONINL
+#include "mem0mem.ic"
+#endif
+
+#include "buf0buf.h"
+#include "srv0srv.h"
+#include "mem0dbg.c"
+#include <stdarg.h>
+
+/*
+ THE MEMORY MANAGEMENT
+ =====================
+
+The basic element of the memory management is called a memory
+heap. A memory heap is conceptually a
+stack from which memory can be allocated. The stack may grow infinitely.
+The top element of the stack may be freed, or
+the whole stack can be freed at one time. The advantage of the
+memory heap concept is that we can avoid using the malloc and free
+functions of C which are quite expensive, for example, on the Solaris + GCC
+system (50 MHz Sparc, 1993) the pair takes 3 microseconds,
+on Win NT + 100MHz Pentium, 2.5 microseconds.
+When we use a memory heap,
+we can allocate larger blocks of memory at a time and thus
+reduce overhead. Slightly more efficient the method is when we
+allocate the memory from the index page buffer pool, as we can
+claim a new page fast. This is called buffer allocation.
+When we allocate the memory from the dynamic memory of the
+C environment, that is called dynamic allocation.
+
+The default way of operation of the memory heap is the following.
+First, when the heap is created, an initial block of memory is
+allocated. In dynamic allocation this may be about 50 bytes.
+If more space is needed, additional blocks are allocated
+and they are put into a linked list.
+After the initial block, each allocated block is twice the size of the
+previous, until a threshold is attained, after which the sizes
+of the blocks stay the same. An exception is, of course, the case
+where the caller requests a memory buffer whose size is
+bigger than the threshold. In that case a block big enough must
+be allocated.
+
+The heap is physically arranged so that if the current block
+becomes full, a new block is allocated and always inserted in the
+chain of blocks as the last block.
+
+In the debug version of the memory management, all the allocated
+heaps are kept in a list (which is implemented as a hash table).
+Thus we can notice if the caller tries to free an already freed
+heap. In addition, each buffer given to the caller contains
+start field at the start and a trailer field at the end of the buffer.
+
+The start field has the following content:
+A. sizeof(ulint) bytes of field length (in the standard byte order)
+B. sizeof(ulint) bytes of check field (a random number)
+
+The trailer field contains:
+A. sizeof(ulint) bytes of check field (the same random number as at the start)
+
+Thus we can notice if something has been copied over the
+borders of the buffer, which is illegal.
+The memory in the buffers is initialized to a random byte sequence.
+After freeing, all the blocks in the heap are set to random bytes
+to help us discover errors which result from the use of
+buffers in an already freed heap. */
+
+#ifdef MEM_PERIODIC_CHECK
+
+ibool mem_block_list_inited;
+/* List of all mem blocks allocated; protected by the mem_comm_pool mutex */
+UT_LIST_BASE_NODE_T(mem_block_t) mem_block_list;
+
+#endif
+
+/**********************************************************************//**
+Duplicates a NUL-terminated string, allocated from a memory heap.
+@return own: a copy of the string */
+UNIV_INTERN
+char*
+mem_heap_strdup(
+/*============*/
+ mem_heap_t* heap, /*!< in: memory heap where string is allocated */
+ const char* str) /*!< in: string to be copied */
+{
+ return(mem_heap_dup(heap, str, strlen(str) + 1));
+}
+
+/**********************************************************************//**
+Duplicate a block of data, allocated from a memory heap.
+@return own: a copy of the data */
+UNIV_INTERN
+void*
+mem_heap_dup(
+/*=========*/
+ mem_heap_t* heap, /*!< in: memory heap where copy is allocated */
+ const void* data, /*!< in: data to be copied */
+ ulint len) /*!< in: length of data, in bytes */
+{
+ return(memcpy(mem_heap_alloc(heap, len), data, len));
+}
+
+/**********************************************************************//**
+Concatenate two strings and return the result, using a memory heap.
+@return own: the result */
+UNIV_INTERN
+char*
+mem_heap_strcat(
+/*============*/
+ mem_heap_t* heap, /*!< in: memory heap where string is allocated */
+ const char* s1, /*!< in: string 1 */
+ const char* s2) /*!< in: string 2 */
+{
+ char* s;
+ ulint s1_len = strlen(s1);
+ ulint s2_len = strlen(s2);
+
+ s = mem_heap_alloc(heap, s1_len + s2_len + 1);
+
+ memcpy(s, s1, s1_len);
+ memcpy(s + s1_len, s2, s2_len);
+
+ s[s1_len + s2_len] = '\0';
+
+ return(s);
+}
+
+
+/****************************************************************//**
+Helper function for mem_heap_printf.
+@return length of formatted string, including terminating NUL */
+static
+ulint
+mem_heap_printf_low(
+/*================*/
+ char* buf, /*!< in/out: buffer to store formatted string
+ in, or NULL to just calculate length */
+ const char* format, /*!< in: format string */
+ va_list ap) /*!< in: arguments */
+{
+ ulint len = 0;
+
+ while (*format) {
+
+ /* Does this format specifier have the 'l' length modifier. */
+ ibool is_long = FALSE;
+
+ /* Length of one parameter. */
+ size_t plen;
+
+ if (*format++ != '%') {
+ /* Non-format character. */
+
+ len++;
+
+ if (buf) {
+ *buf++ = *(format - 1);
+ }
+
+ continue;
+ }
+
+ if (*format == 'l') {
+ is_long = TRUE;
+ format++;
+ }
+
+ switch (*format++) {
+ case 's':
+ /* string */
+ {
+ char* s = va_arg(ap, char*);
+
+ /* "%ls" is a non-sensical format specifier. */
+ ut_a(!is_long);
+
+ plen = strlen(s);
+ len += plen;
+
+ if (buf) {
+ memcpy(buf, s, plen);
+ buf += plen;
+ }
+ }
+
+ break;
+
+ case 'u':
+ /* unsigned int */
+ {
+ char tmp[32];
+ unsigned long val;
+
+ /* We only support 'long' values for now. */
+ ut_a(is_long);
+
+ val = va_arg(ap, unsigned long);
+
+ plen = sprintf(tmp, "%lu", val);
+ len += plen;
+
+ if (buf) {
+ memcpy(buf, tmp, plen);
+ buf += plen;
+ }
+ }
+
+ break;
+
+ case '%':
+
+ /* "%l%" is a non-sensical format specifier. */
+ ut_a(!is_long);
+
+ len++;
+
+ if (buf) {
+ *buf++ = '%';
+ }
+
+ break;
+
+ default:
+ ut_error;
+ }
+ }
+
+ /* For the NUL character. */
+ len++;
+
+ if (buf) {
+ *buf = '\0';
+ }
+
+ return(len);
+}
+
+/****************************************************************//**
+A simple (s)printf replacement that dynamically allocates the space for the
+formatted string from the given heap. This supports a very limited set of
+the printf syntax: types 's' and 'u' and length modifier 'l' (which is
+required for the 'u' type).
+@return heap-allocated formatted string */
+UNIV_INTERN
+char*
+mem_heap_printf(
+/*============*/
+ mem_heap_t* heap, /*!< in: memory heap */
+ const char* format, /*!< in: format string */
+ ...)
+{
+ va_list ap;
+ char* str;
+ ulint len;
+
+ /* Calculate length of string */
+ len = 0;
+ va_start(ap, format);
+ len = mem_heap_printf_low(NULL, format, ap);
+ va_end(ap);
+
+ /* Now create it for real. */
+ str = mem_heap_alloc(heap, len);
+ va_start(ap, format);
+ mem_heap_printf_low(str, format, ap);
+ va_end(ap);
+
+ return(str);
+}
+
+/***************************************************************//**
+Creates a memory heap block where data can be allocated.
+@return own: memory heap block, NULL if did not succeed (only possible
+for MEM_HEAP_BTR_SEARCH type heaps) */
+UNIV_INTERN
+mem_block_t*
+mem_heap_create_block(
+/*==================*/
+ mem_heap_t* heap, /*!< in: memory heap or NULL if first block
+ should be created */
+ ulint n, /*!< in: number of bytes needed for user data */
+ ulint type, /*!< in: type of heap: MEM_HEAP_DYNAMIC or
+ MEM_HEAP_BUFFER */
+ const char* file_name,/*!< in: file name where created */
+ ulint line) /*!< in: line where created */
+{
+#ifndef UNIV_HOTBACKUP
+ buf_block_t* buf_block = NULL;
+#endif /* !UNIV_HOTBACKUP */
+ mem_block_t* block;
+ ulint len;
+
+ ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER)
+ || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH));
+
+ if (heap && heap->magic_n != MEM_BLOCK_MAGIC_N) {
+ mem_analyze_corruption(heap);
+ }
+
+ /* In dynamic allocation, calculate the size: block header + data. */
+ len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
+
+#ifndef UNIV_HOTBACKUP
+ if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) {
+
+ ut_ad(type == MEM_HEAP_DYNAMIC || n <= MEM_MAX_ALLOC_IN_BUF);
+
+ block = mem_area_alloc(&len, mem_comm_pool);
+ } else {
+ len = UNIV_PAGE_SIZE;
+
+ if ((type & MEM_HEAP_BTR_SEARCH) && heap) {
+ /* We cannot allocate the block from the
+ buffer pool, but must get the free block from
+ the heap header free block field */
+
+ buf_block = heap->free_block;
+ heap->free_block = NULL;
+
+ if (UNIV_UNLIKELY(!buf_block)) {
+
+ return(NULL);
+ }
+ } else {
+ buf_block = buf_block_alloc(0);
+ }
+
+ block = (mem_block_t*) buf_block->frame;
+ }
+
+ ut_ad(block);
+ block->buf_block = buf_block;
+ block->free_block = NULL;
+#else /* !UNIV_HOTBACKUP */
+ len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
+ block = ut_malloc(len);
+ ut_ad(block);
+#endif /* !UNIV_HOTBACKUP */
+
+ block->magic_n = MEM_BLOCK_MAGIC_N;
+ ut_strlcpy_rev(block->file_name, file_name, sizeof(block->file_name));
+ block->line = line;
+
+#ifdef MEM_PERIODIC_CHECK
+ mem_pool_mutex_enter();
+
+ if (!mem_block_list_inited) {
+ mem_block_list_inited = TRUE;
+ UT_LIST_INIT(mem_block_list);
+ }
+
+ UT_LIST_ADD_LAST(mem_block_list, mem_block_list, block);
+
+ mem_pool_mutex_exit();
+#endif
+ mem_block_set_len(block, len);
+ mem_block_set_type(block, type);
+ mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE);
+ mem_block_set_start(block, MEM_BLOCK_HEADER_SIZE);
+
+ ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len);
+
+ return(block);
+}
+
+/***************************************************************//**
+Adds a new block to a memory heap.
+@return created block, NULL if did not succeed (only possible for
+MEM_HEAP_BTR_SEARCH type heaps) */
+UNIV_INTERN
+mem_block_t*
+mem_heap_add_block(
+/*===============*/
+ mem_heap_t* heap, /*!< in: memory heap */
+ ulint n) /*!< in: number of bytes user needs */
+{
+ mem_block_t* block;
+ mem_block_t* new_block;
+ ulint new_size;
+
+ ut_ad(mem_heap_check(heap));
+
+ block = UT_LIST_GET_LAST(heap->base);
+
+ /* We have to allocate a new block. The size is always at least
+ doubled until the standard size is reached. After that the size
+ stays the same, except in cases where the caller needs more space. */
+
+ new_size = 2 * mem_block_get_len(block);
+
+ if (heap->type != MEM_HEAP_DYNAMIC) {
+ /* From the buffer pool we allocate buffer frames */
+ ut_a(n <= MEM_MAX_ALLOC_IN_BUF);
+
+ if (new_size > MEM_MAX_ALLOC_IN_BUF) {
+ new_size = MEM_MAX_ALLOC_IN_BUF;
+ }
+ } else if (new_size > MEM_BLOCK_STANDARD_SIZE) {
+
+ new_size = MEM_BLOCK_STANDARD_SIZE;
+ }
+
+ if (new_size < n) {
+ new_size = n;
+ }
+
+ new_block = mem_heap_create_block(heap, new_size, heap->type,
+ heap->file_name, heap->line);
+ if (new_block == NULL) {
+
+ return(NULL);
+ }
+
+ /* Add the new block as the last block */
+
+ UT_LIST_INSERT_AFTER(list, heap->base, block, new_block);
+
+ return(new_block);
+}
+
+/******************************************************************//**
+Frees a block from a memory heap. */
+UNIV_INTERN
+void
+mem_heap_block_free(
+/*================*/
+ mem_heap_t* heap, /*!< in: heap */
+ mem_block_t* block) /*!< in: block to free */
+{
+ ulint type;
+ ulint len;
+#ifndef UNIV_HOTBACKUP
+ buf_block_t* buf_block = block->buf_block;
+#endif /* !UNIV_HOTBACKUP */
+
+ if (block->magic_n != MEM_BLOCK_MAGIC_N) {
+ mem_analyze_corruption(block);
+ }
+
+ UT_LIST_REMOVE(list, heap->base, block);
+
+#ifdef MEM_PERIODIC_CHECK
+ mem_pool_mutex_enter();
+
+ UT_LIST_REMOVE(mem_block_list, mem_block_list, block);
+
+ mem_pool_mutex_exit();
+#endif
+ type = heap->type;
+ len = block->len;
+ block->magic_n = MEM_FREED_BLOCK_MAGIC_N;
+
+#ifdef UNIV_MEM_DEBUG
+ /* In the debug version we set the memory to a random combination
+ of hex 0xDE and 0xAD. */
+
+ mem_erase_buf((byte*)block, len);
+#else /* UNIV_MEM_DEBUG */
+ UNIV_MEM_ASSERT_AND_FREE(block, len);
+#endif /* UNIV_MEM_DEBUG */
+
+#ifndef UNIV_HOTBACKUP
+ if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) {
+
+ ut_ad(!buf_block);
+ mem_area_free(block, mem_comm_pool);
+ } else {
+ ut_ad(type & MEM_HEAP_BUFFER);
+
+ buf_block_free(buf_block);
+ }
+#else /* !UNIV_HOTBACKUP */
+ ut_free(block);
+#endif /* !UNIV_HOTBACKUP */
+}
+
+#ifndef UNIV_HOTBACKUP
+/******************************************************************//**
+Frees the free_block field from a memory heap. */
+UNIV_INTERN
+void
+mem_heap_free_block_free(
+/*=====================*/
+ mem_heap_t* heap) /*!< in: heap */
+{
+ if (UNIV_LIKELY_NULL(heap->free_block)) {
+
+ buf_block_free(heap->free_block);
+
+ heap->free_block = NULL;
+ }
+}
+#endif /* !UNIV_HOTBACKUP */
+
+#ifdef MEM_PERIODIC_CHECK
+/******************************************************************//**
+Goes through the list of all allocated mem blocks, checks their magic
+numbers, and reports possible corruption. */
+UNIV_INTERN
+void
+mem_validate_all_blocks(void)
+/*=========================*/
+{
+ mem_block_t* block;
+
+ mem_pool_mutex_enter();
+
+ block = UT_LIST_GET_FIRST(mem_block_list);
+
+ while (block) {
+ if (block->magic_n != MEM_BLOCK_MAGIC_N) {
+ mem_analyze_corruption(block);
+ }
+
+ block = UT_LIST_GET_NEXT(mem_block_list, block);
+ }
+
+ mem_pool_mutex_exit();
+}
+#endif
diff --git a/storage/innobase/mem/mem0pool.c b/storage/innobase/mem/mem0pool.c
new file mode 100644
index 00000000000..c8fea97a6a3
--- /dev/null
+++ b/storage/innobase/mem/mem0pool.c
@@ -0,0 +1,705 @@
+/*****************************************************************************
+
+Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+*****************************************************************************/
+
+/********************************************************************//**
+@file mem/mem0pool.c
+The lowest-level memory management
+
+Created 5/12/1997 Heikki Tuuri
+*************************************************************************/
+
+#include "mem0pool.h"
+#ifdef UNIV_NONINL
+#include "mem0pool.ic"
+#endif
+
+#include "srv0srv.h"
+#include "sync0sync.h"
+#include "ut0mem.h"
+#include "ut0lst.h"
+#include "ut0byte.h"
+#include "mem0mem.h"
+
+/* We would like to use also the buffer frames to allocate memory. This
+would be desirable, because then the memory consumption of the database
+would be fixed, and we might even lock the buffer pool to the main memory.
+The problem here is that the buffer management routines can themselves call
+memory allocation, while the buffer pool mutex is reserved.
+
+The main components of the memory consumption are:
+
+1. buffer pool,
+2. parsed and optimized SQL statements,
+3. data dictionary cache,
+4. log buffer,
+5. locks for each transaction,
+6. hash table for the adaptive index,
+7. state and buffers for each SQL query currently being executed,
+8. session for each user, and
+9. stack for each OS thread.
+
+Items 1 and 2 are managed by an LRU algorithm. Items 5 and 6 can potentially
+consume very much memory. Items 7 and 8 should consume quite little memory,
+and the OS should take care of item 9, which too should consume little memory.
+
+A solution to the memory management:
+
+1. the buffer pool size is set separately;
+2. log buffer size is set separately;
+3. the common pool size for all the other entries, except 8, is set separately.
+
+Problems: we may waste memory if the common pool is set too big. Another
+problem is the locks, which may take very much space in big transactions.
+Then the shared pool size should be set very big. We can allow locks to take
+space from the buffer pool, but the SQL optimizer is then unaware of the
+usable size of the buffer pool. We could also combine the objects in the
+common pool and the buffers in the buffer pool into a single LRU list and
+manage it uniformly, but this approach does not take into account the parsing
+and other costs unique to SQL statements.
+
+The locks for a transaction can be seen as a part of the state of the
+transaction. Hence, they should be stored in the common pool. We still
+have the problem of a very big update transaction, for example, which
+will set very many x-locks on rows, and the locks will consume a lot
+of memory, say, half of the buffer pool size.
+
+Another problem is what to do if we are not able to malloc a requested
+block of memory from the common pool. Then we can request memory from
+the operating system. If it does not help, a system error results.
+
+Because 5 and 6 may potentially consume very much memory, we let them grow
+into the buffer pool. We may let the locks of a transaction take frames
+from the buffer pool, when the corresponding memory heap block has grown to
+the size of a buffer frame. Similarly for the hash node cells of the locks,
+and for the adaptive index. Thus, for each individual transaction, its locks
+can occupy at most about the size of the buffer frame of memory in the common
+pool, and after that its locks will grow into the buffer pool. */
+
+/** Mask used to extract the free bit from area->size */
+#define MEM_AREA_FREE 1
+
+/** The smallest memory area total size */
+#define MEM_AREA_MIN_SIZE (2 * MEM_AREA_EXTRA_SIZE)
+
+
+/** Data structure for a memory pool. The space is allocated using the buddy
+algorithm, where free list i contains areas of size 2 to power i. */
+struct mem_pool_struct{
+ byte* buf; /*!< memory pool */
+ ulint size; /*!< memory common pool size */
+ ulint reserved; /*!< amount of currently allocated
+ memory */
+ mutex_t mutex; /*!< mutex protecting this struct */
+ UT_LIST_BASE_NODE_T(mem_area_t)
+ free_list[64]; /*!< lists of free memory areas: an
+ area is put to the list whose number
+ is the 2-logarithm of the area size */
+};
+
+/** The common memory pool */
+UNIV_INTERN mem_pool_t* mem_comm_pool = NULL;
+
+/* We use this counter to check that the mem pool mutex does not leak;
+this is to track a strange assertion failure reported at
+mysql@lists.mysql.com */
+
+UNIV_INTERN ulint mem_n_threads_inside = 0;
+
+/********************************************************************//**
+Reserves the mem pool mutex. */
+UNIV_INTERN
+void
+mem_pool_mutex_enter(void)
+/*======================*/
+{
+ mutex_enter(&(mem_comm_pool->mutex));
+}
+
+/********************************************************************//**
+Releases the mem pool mutex. */
+UNIV_INTERN
+void
+mem_pool_mutex_exit(void)
+/*=====================*/
+{
+ mutex_exit(&(mem_comm_pool->mutex));
+}
+
+/********************************************************************//**
+Returns memory area size.
+@return size */
+UNIV_INLINE
+ulint
+mem_area_get_size(
+/*==============*/
+ mem_area_t* area) /*!< in: area */
+{
+ return(area->size_and_free & ~MEM_AREA_FREE);
+}
+
+/********************************************************************//**
+Sets memory area size. */
+UNIV_INLINE
+void
+mem_area_set_size(
+/*==============*/
+ mem_area_t* area, /*!< in: area */
+ ulint size) /*!< in: size */
+{
+ area->size_and_free = (area->size_and_free & MEM_AREA_FREE)
+ | size;
+}
+
+/********************************************************************//**
+Returns memory area free bit.
+@return TRUE if free */
+UNIV_INLINE
+ibool
+mem_area_get_free(
+/*==============*/
+ mem_area_t* area) /*!< in: area */
+{
+#if TRUE != MEM_AREA_FREE
+# error "TRUE != MEM_AREA_FREE"
+#endif
+ return(area->size_and_free & MEM_AREA_FREE);
+}
+
+/********************************************************************//**
+Sets memory area free bit. */
+UNIV_INLINE
+void
+mem_area_set_free(
+/*==============*/
+ mem_area_t* area, /*!< in: area */
+ ibool free) /*!< in: free bit value */
+{
+#if TRUE != MEM_AREA_FREE
+# error "TRUE != MEM_AREA_FREE"
+#endif
+ area->size_and_free = (area->size_and_free & ~MEM_AREA_FREE)
+ | free;
+}
+
+/********************************************************************//**
+Creates a memory pool.
+@return memory pool */
+UNIV_INTERN
+mem_pool_t*
+mem_pool_create(
+/*============*/
+ ulint size) /*!< in: pool size in bytes */
+{
+ mem_pool_t* pool;
+ mem_area_t* area;
+ ulint i;
+ ulint used;
+
+ pool = ut_malloc(sizeof(mem_pool_t));
+
+ /* We do not set the memory to zero (FALSE) in the pool,
+ but only when allocated at a higher level in mem0mem.c.
+ This is to avoid masking useful Purify warnings. */
+
+ pool->buf = ut_malloc_low(size, FALSE, TRUE);
+ pool->size = size;
+
+ mutex_create(&pool->mutex, SYNC_MEM_POOL);
+
+ /* Initialize the free lists */
+
+ for (i = 0; i < 64; i++) {
+
+ UT_LIST_INIT(pool->free_list[i]);
+ }
+
+ used = 0;
+
+ while (size - used >= MEM_AREA_MIN_SIZE) {
+
+ i = ut_2_log(size - used);
+
+ if (ut_2_exp(i) > size - used) {
+
+ /* ut_2_log rounds upward */
+
+ i--;
+ }
+
+ area = (mem_area_t*)(pool->buf + used);
+
+ mem_area_set_size(area, ut_2_exp(i));
+ mem_area_set_free(area, TRUE);
+ UNIV_MEM_FREE(MEM_AREA_EXTRA_SIZE + (byte*) area,
+ ut_2_exp(i) - MEM_AREA_EXTRA_SIZE);
+
+ UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area);
+
+ used = used + ut_2_exp(i);
+ }
+
+ ut_ad(size >= used);
+
+ pool->reserved = 0;
+
+ return(pool);
+}
+
+/********************************************************************//**
+Fills the specified free list.
+@return TRUE if we were able to insert a block to the free list */
+static
+ibool
+mem_pool_fill_free_list(
+/*====================*/
+ ulint i, /*!< in: free list index */
+ mem_pool_t* pool) /*!< in: memory pool */
+{
+ mem_area_t* area;
+ mem_area_t* area2;
+ ibool ret;
+
+ ut_ad(mutex_own(&(pool->mutex)));
+
+ if (UNIV_UNLIKELY(i >= 63)) {
+ /* We come here when we have run out of space in the
+ memory pool: */
+
+ return(FALSE);
+ }
+
+ area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
+
+ if (area == NULL) {
+ if (UT_LIST_GET_LEN(pool->free_list[i + 1]) > 0) {
+ ut_print_timestamp(stderr);
+
+ fprintf(stderr,
+ " InnoDB: Error: mem pool free list %lu"
+ " length is %lu\n"
+ "InnoDB: though the list is empty!\n",
+ (ulong) i + 1,
+ (ulong)
+ UT_LIST_GET_LEN(pool->free_list[i + 1]));
+ }
+
+ ret = mem_pool_fill_free_list(i + 1, pool);
+
+ if (ret == FALSE) {
+
+ return(FALSE);
+ }
+
+ area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
+ }
+
+ if (UNIV_UNLIKELY(UT_LIST_GET_LEN(pool->free_list[i + 1]) == 0)) {
+ mem_analyze_corruption(area);
+
+ ut_error;
+ }
+
+ UT_LIST_REMOVE(free_list, pool->free_list[i + 1], area);
+
+ area2 = (mem_area_t*)(((byte*)area) + ut_2_exp(i));
+ UNIV_MEM_ALLOC(area2, MEM_AREA_EXTRA_SIZE);
+
+ mem_area_set_size(area2, ut_2_exp(i));
+ mem_area_set_free(area2, TRUE);
+
+ UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area2);
+
+ mem_area_set_size(area, ut_2_exp(i));
+
+ UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area);
+
+ return(TRUE);
+}
+
+/********************************************************************//**
+Allocates memory from a pool. NOTE: This low-level function should only be
+used in mem0mem.*!
+@return own: allocated memory buffer */
+UNIV_INTERN
+void*
+mem_area_alloc(
+/*===========*/
+ ulint* psize, /*!< in: requested size in bytes; for optimum
+ space usage, the size should be a power of 2
+ minus MEM_AREA_EXTRA_SIZE;
+ out: allocated size in bytes (greater than
+ or equal to the requested size) */
+ mem_pool_t* pool) /*!< in: memory pool */
+{
+ mem_area_t* area;
+ ulint size;
+ ulint n;
+ ibool ret;
+
+ /* If we are using os allocator just make a simple call
+ to malloc */
+ if (UNIV_LIKELY(srv_use_sys_malloc)) {
+ return(malloc(*psize));
+ }
+
+ size = *psize;
+ n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE));
+
+ mutex_enter(&(pool->mutex));
+ mem_n_threads_inside++;
+
+ ut_a(mem_n_threads_inside == 1);
+
+ area = UT_LIST_GET_FIRST(pool->free_list[n]);
+
+ if (area == NULL) {
+ ret = mem_pool_fill_free_list(n, pool);
+
+ if (ret == FALSE) {
+ /* Out of memory in memory pool: we try to allocate
+ from the operating system with the regular malloc: */
+
+ mem_n_threads_inside--;
+ mutex_exit(&(pool->mutex));
+
+ return(ut_malloc(size));
+ }
+
+ area = UT_LIST_GET_FIRST(pool->free_list[n]);
+ }
+
+ if (!mem_area_get_free(area)) {
+ fprintf(stderr,
+ "InnoDB: Error: Removing element from mem pool"
+ " free list %lu though the\n"
+ "InnoDB: element is not marked free!\n",
+ (ulong) n);
+
+ mem_analyze_corruption(area);
+
+ /* Try to analyze a strange assertion failure reported at
+ mysql@lists.mysql.com where the free bit IS 1 in the
+ hex dump above */
+
+ if (mem_area_get_free(area)) {
+ fprintf(stderr,
+ "InnoDB: Probably a race condition"
+ " because now the area is marked free!\n");
+ }
+
+ ut_error;
+ }
+
+ if (UT_LIST_GET_LEN(pool->free_list[n]) == 0) {
+ fprintf(stderr,
+ "InnoDB: Error: Removing element from mem pool"
+ " free list %lu\n"
+ "InnoDB: though the list length is 0!\n",
+ (ulong) n);
+ mem_analyze_corruption(area);
+
+ ut_error;
+ }
+
+ ut_ad(mem_area_get_size(area) == ut_2_exp(n));
+
+ mem_area_set_free(area, FALSE);
+
+ UT_LIST_REMOVE(free_list, pool->free_list[n], area);
+
+ pool->reserved += mem_area_get_size(area);
+
+ mem_n_threads_inside--;
+ mutex_exit(&(pool->mutex));
+
+ ut_ad(mem_pool_validate(pool));
+
+ *psize = ut_2_exp(n) - MEM_AREA_EXTRA_SIZE;
+ UNIV_MEM_ALLOC(MEM_AREA_EXTRA_SIZE + (byte*)area, *psize);
+
+ return((void*)(MEM_AREA_EXTRA_SIZE + ((byte*)area)));
+}
+
+/********************************************************************//**
+Gets the buddy of an area, if it exists in pool.
+@return the buddy, NULL if no buddy in pool */
+UNIV_INLINE
+mem_area_t*
+mem_area_get_buddy(
+/*===============*/
+ mem_area_t* area, /*!< in: memory area */
+ ulint size, /*!< in: memory area size */
+ mem_pool_t* pool) /*!< in: memory pool */
+{
+ mem_area_t* buddy;
+
+ ut_ad(size != 0);
+
+ if (((((byte*)area) - pool->buf) % (2 * size)) == 0) {
+
+ /* The buddy is in a higher address */
+
+ buddy = (mem_area_t*)(((byte*)area) + size);
+
+ if ((((byte*)buddy) - pool->buf) + size > pool->size) {
+
+ /* The buddy is not wholly contained in the pool:
+ there is no buddy */
+
+ buddy = NULL;
+ }
+ } else {
+ /* The buddy is in a lower address; NOTE that area cannot
+ be at the pool lower end, because then we would end up to
+ the upper branch in this if-clause: the remainder would be
+ 0 */
+
+ buddy = (mem_area_t*)(((byte*)area) - size);
+ }
+
+ return(buddy);
+}
+
+/********************************************************************//**
+Frees memory to a pool. */
+UNIV_INTERN
+void
+mem_area_free(
+/*==========*/
+ void* ptr, /*!< in, own: pointer to allocated memory
+ buffer */
+ mem_pool_t* pool) /*!< in: memory pool */
+{
+ mem_area_t* area;
+ mem_area_t* buddy;
+ void* new_ptr;
+ ulint size;
+ ulint n;
+
+ if (UNIV_LIKELY(srv_use_sys_malloc)) {
+ free(ptr);
+
+ return;
+ }
+
+ /* It may be that the area was really allocated from the OS with
+ regular malloc: check if ptr points within our memory pool */
+
+ if ((byte*)ptr < pool->buf || (byte*)ptr >= pool->buf + pool->size) {
+ ut_free(ptr);
+
+ return;
+ }
+
+ area = (mem_area_t*) (((byte*)ptr) - MEM_AREA_EXTRA_SIZE);
+
+ if (mem_area_get_free(area)) {
+ fprintf(stderr,
+ "InnoDB: Error: Freeing element to mem pool"
+ " free list though the\n"
+ "InnoDB: element is marked free!\n");
+
+ mem_analyze_corruption(area);
+ ut_error;
+ }
+
+ size = mem_area_get_size(area);
+ UNIV_MEM_FREE(ptr, size - MEM_AREA_EXTRA_SIZE);
+
+ if (size == 0) {
+ fprintf(stderr,
+ "InnoDB: Error: Mem area size is 0. Possibly a"
+ " memory overrun of the\n"
+ "InnoDB: previous allocated area!\n");
+
+ mem_analyze_corruption(area);
+ ut_error;
+ }
+
+#ifdef UNIV_LIGHT_MEM_DEBUG
+ if (((byte*)area) + size < pool->buf + pool->size) {
+
+ ulint next_size;
+
+ next_size = mem_area_get_size(
+ (mem_area_t*)(((byte*)area) + size));
+ if (UNIV_UNLIKELY(!next_size || !ut_is_2pow(next_size))) {
+ fprintf(stderr,
+ "InnoDB: Error: Memory area size %lu,"
+ " next area size %lu not a power of 2!\n"
+ "InnoDB: Possibly a memory overrun of"
+ " the buffer being freed here.\n",
+ (ulong) size, (ulong) next_size);
+ mem_analyze_corruption(area);
+
+ ut_error;
+ }
+ }
+#endif
+ buddy = mem_area_get_buddy(area, size, pool);
+
+ n = ut_2_log(size);
+
+ mutex_enter(&(pool->mutex));
+ mem_n_threads_inside++;
+
+ ut_a(mem_n_threads_inside == 1);
+
+ if (buddy && mem_area_get_free(buddy)
+ && (size == mem_area_get_size(buddy))) {
+
+ /* The buddy is in a free list */
+
+ if ((byte*)buddy < (byte*)area) {
+ new_ptr = ((byte*)buddy) + MEM_AREA_EXTRA_SIZE;
+
+ mem_area_set_size(buddy, 2 * size);
+ mem_area_set_free(buddy, FALSE);
+ } else {
+ new_ptr = ptr;
+
+ mem_area_set_size(area, 2 * size);
+ }
+
+ /* Remove the buddy from its free list and merge it to area */
+
+ UT_LIST_REMOVE(free_list, pool->free_list[n], buddy);
+
+ pool->reserved += ut_2_exp(n);
+
+ mem_n_threads_inside--;
+ mutex_exit(&(pool->mutex));
+
+ mem_area_free(new_ptr, pool);
+
+ return;
+ } else {
+ UT_LIST_ADD_FIRST(free_list, pool->free_list[n], area);
+
+ mem_area_set_free(area, TRUE);
+
+ ut_ad(pool->reserved >= size);
+
+ pool->reserved -= size;
+ }
+
+ mem_n_threads_inside--;
+ mutex_exit(&(pool->mutex));
+
+ ut_ad(mem_pool_validate(pool));
+}
+
+/********************************************************************//**
+Validates a memory pool.
+@return TRUE if ok */
+UNIV_INTERN
+ibool
+mem_pool_validate(
+/*==============*/
+ mem_pool_t* pool) /*!< in: memory pool */
+{
+ mem_area_t* area;
+ mem_area_t* buddy;
+ ulint free;
+ ulint i;
+
+ mutex_enter(&(pool->mutex));
+
+ free = 0;
+
+ for (i = 0; i < 64; i++) {
+
+ UT_LIST_VALIDATE(free_list, mem_area_t, pool->free_list[i],
+ (void) 0);
+
+ area = UT_LIST_GET_FIRST(pool->free_list[i]);
+
+ while (area != NULL) {
+ ut_a(mem_area_get_free(area));
+ ut_a(mem_area_get_size(area) == ut_2_exp(i));
+
+ buddy = mem_area_get_buddy(area, ut_2_exp(i), pool);
+
+ ut_a(!buddy || !mem_area_get_free(buddy)
+ || (ut_2_exp(i) != mem_area_get_size(buddy)));
+
+ area = UT_LIST_GET_NEXT(free_list, area);
+
+ free += ut_2_exp(i);
+ }
+ }
+
+ ut_a(free + pool->reserved == pool->size);
+
+ mutex_exit(&(pool->mutex));
+
+ return(TRUE);
+}
+
+/********************************************************************//**
+Prints info of a memory pool. */
+UNIV_INTERN
+void
+mem_pool_print_info(
+/*================*/
+ FILE* outfile,/*!< in: output file to write to */
+ mem_pool_t* pool) /*!< in: memory pool */
+{
+ ulint i;
+
+ mem_pool_validate(pool);
+
+ fprintf(outfile, "INFO OF A MEMORY POOL\n");
+
+ mutex_enter(&(pool->mutex));
+
+ for (i = 0; i < 64; i++) {
+ if (UT_LIST_GET_LEN(pool->free_list[i]) > 0) {
+
+ fprintf(outfile,
+ "Free list length %lu for"
+ " blocks of size %lu\n",
+ (ulong) UT_LIST_GET_LEN(pool->free_list[i]),
+ (ulong) ut_2_exp(i));
+ }
+ }
+
+ fprintf(outfile, "Pool size %lu, reserved %lu.\n", (ulong) pool->size,
+ (ulong) pool->reserved);
+ mutex_exit(&(pool->mutex));
+}
+
+/********************************************************************//**
+Returns the amount of reserved memory.
+@return reserved memory in bytes */
+UNIV_INTERN
+ulint
+mem_pool_get_reserved(
+/*==================*/
+ mem_pool_t* pool) /*!< in: memory pool */
+{
+ ulint reserved;
+
+ mutex_enter(&(pool->mutex));
+
+ reserved = pool->reserved;
+
+ mutex_exit(&(pool->mutex));
+
+ return(reserved);
+}