diff options
Diffstat (limited to 'storage/innobase/mem')
-rw-r--r-- | storage/innobase/mem/mem0dbg.cc | 1050 | ||||
-rw-r--r-- | storage/innobase/mem/mem0mem.cc | 231 | ||||
-rw-r--r-- | storage/innobase/mem/mem0pool.cc | 727 |
3 files changed, 64 insertions, 1944 deletions
diff --git a/storage/innobase/mem/mem0dbg.cc b/storage/innobase/mem/mem0dbg.cc deleted file mode 100644 index a77785a369a..00000000000 --- a/storage/innobase/mem/mem0dbg.cc +++ /dev/null @@ -1,1050 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1994, 2016, Oracle and/or its affiliates. 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., -51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/********************************************************************//** -@file mem/mem0dbg.cc -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 -# include "ha_prototypes.h" -/* 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 ib_mutex_t mem_hash_mutex; - -#ifdef UNIV_PFS_MUTEX -/* Key to register mem_hash_mutex with performance schema */ -UNIV_INTERN mysql_pfs_key_t mem_hash_mutex_key; -#endif /* UNIV_PFS_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 */ - -struct mem_hash_node_t { - 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_key, &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); -} - -/******************************************************************//** -Closes the memory system. */ -UNIV_INTERN -void -mem_close(void) -/*===========*/ -{ - mem_pool_free(mem_comm_pool); - mem_comm_pool = NULL; -#ifdef UNIV_MEM_DEBUG - mutex_free(&mem_hash_mutex); - mem_hash_initialized = FALSE; -#endif /* UNIV_MEM_DEBUG */ -} -#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 MY_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 = static_cast<mem_hash_node_t*>(ut_malloc(sizeof(*new_node))); - - 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", - innobase_basename(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, - innobase_basename(node->file_name), (ulong) node->line, - innobase_basename(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 MY_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", - innobase_basename(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, - innobase_basename(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.cc b/storage/innobase/mem/mem0mem.cc index e066aff5b30..41292ccf842 100644 --- a/storage/innobase/mem/mem0mem.cc +++ b/storage/innobase/mem/mem0mem.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1994, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1994, 2014, Oracle and/or its affiliates. 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 @@ -23,6 +23,8 @@ The memory management Created 6/9/1994 Heikki Tuuri *************************************************************************/ +#include "ha_prototypes.h" + #include "mem0mem.h" #ifdef UNIV_NONINL #include "mem0mem.ic" @@ -30,91 +32,23 @@ Created 6/9/1994 Heikki Tuuri #include "buf0buf.h" #include "srv0srv.h" -#include "mem0dbg.cc" #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 +/** Duplicates a NUL-terminated string, allocated from a memory heap. +@param[in] heap, memory heap where string is allocated +@param[in] str) string to be copied +@return own: a copy of the string */ char* mem_heap_strdup( -/*============*/ - mem_heap_t* heap, /*!< in: memory heap where string is allocated */ - const char* str) /*!< in: string to be copied */ + mem_heap_t* heap, + const char* str) { return(static_cast<char*>(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 +@return own: a copy of the data */ void* mem_heap_dup( /*=========*/ @@ -127,8 +61,7 @@ mem_heap_dup( /**********************************************************************//** Concatenate two strings and return the result, using a memory heap. -@return own: the result */ -UNIV_INTERN +@return own: the result */ char* mem_heap_strcat( /*============*/ @@ -153,7 +86,7 @@ mem_heap_strcat( /****************************************************************//** Helper function for mem_heap_printf. -@return length of formatted string, including terminating NUL */ +@return length of formatted string, including terminating NUL */ static ulint mem_heap_printf_low( @@ -265,8 +198,7 @@ A simple sprintf 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 +@return heap-allocated formatted string */ char* mem_heap_printf( /*============*/ @@ -293,11 +225,45 @@ mem_heap_printf( return(str); } +#ifdef UNIV_DEBUG +/** Validates the contents of a memory heap. +Checks a memory heap for consistency, prints the contents if any error +is detected. A fatal error is logged if an error is detected. +@param[in] heap Memory heap to validate. */ +void +mem_heap_validate( + const mem_heap_t* heap) +{ + ulint size = 0; + + for (const mem_block_t* block = heap; + block != NULL; + block = UT_LIST_GET_NEXT(list, block)) { + + mem_block_validate(block); + + switch (block->type) { + case MEM_HEAP_DYNAMIC: + break; + case MEM_HEAP_BUFFER: + case MEM_HEAP_BUFFER | MEM_HEAP_BTR_SEARCH: + ut_ad(block->len <= UNIV_PAGE_SIZE); + break; + default: + ut_error; + } + + size += block->len; + } + + ut_ad(size == heap->total_size); +} +#endif /* UNIV_DEBUG */ + /***************************************************************//** 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_func( /*=======================*/ @@ -320,8 +286,9 @@ mem_heap_create_block_func( 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); + if (heap != NULL) { + mem_block_validate(heap); + ut_d(mem_heap_validate(heap)); } /* In dynamic allocation, calculate the size: block header + data. */ @@ -332,8 +299,7 @@ mem_heap_create_block_func( ut_ad(type == MEM_HEAP_DYNAMIC || n <= MEM_MAX_ALLOC_IN_BUF); - block = static_cast<mem_block_t*>( - mem_area_alloc(&len, mem_comm_pool)); + block = static_cast<mem_block_t*>(ut_malloc_nokey(len)); } else { len = UNIV_PAGE_SIZE; @@ -356,16 +322,16 @@ mem_heap_create_block_func( block = (mem_block_t*) buf_block->frame; } - if(!block) { - ib_logf(IB_LOG_LEVEL_FATAL, - " InnoDB: Unable to allocate memory of size %lu.\n", - len); + if (block == NULL) { + ib::fatal() << "Unable to allocate memory of size " + << len << "."; } + 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); + block = ut_malloc_nokey(len); ut_ad(block); #endif /* !UNIV_HOTBACKUP */ @@ -374,18 +340,6 @@ mem_heap_create_block_func( sizeof(block->file_name))); ut_d(block->line = line); -#ifdef MEM_PERIODIC_CHECK - mutex_enter(&(mem_comm_pool->mutex)); - - 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); - - mutex_exit(&(mem_comm_pool->mutex)); -#endif mem_block_set_len(block, len); mem_block_set_type(block, type); mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE); @@ -414,7 +368,6 @@ mem_heap_create_block_func( 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( /*===============*/ @@ -425,7 +378,7 @@ mem_heap_add_block( mem_block_t* new_block; ulint new_size; - ut_ad(mem_heap_check(heap)); + ut_d(mem_block_validate(heap)); block = UT_LIST_GET_LAST(heap->base); @@ -460,14 +413,13 @@ mem_heap_add_block( /* Add the new block as the last block */ - UT_LIST_INSERT_AFTER(list, heap->base, block, new_block); + UT_LIST_INSERT_AFTER(heap->base, block, new_block); return(new_block); } /******************************************************************//** Frees a block from a memory heap. */ -UNIV_INTERN void mem_heap_block_free( /*================*/ @@ -482,19 +434,9 @@ mem_heap_block_free( buf_block = static_cast<buf_block_t*>(block->buf_block); #endif /* !UNIV_HOTBACKUP */ - if (block->magic_n != MEM_BLOCK_MAGIC_N) { - mem_analyze_corruption(block); - } + mem_block_validate(block); - UT_LIST_REMOVE(list, heap->base, block); - -#ifdef MEM_PERIODIC_CHECK - mutex_enter(&(mem_comm_pool->mutex)); - - UT_LIST_REMOVE(mem_block_list, mem_block_list, block); - - mutex_exit(&(mem_comm_pool->mutex)); -#endif + UT_LIST_REMOVE(heap->base, block); ut_ad(heap->total_size >= block->len); heap->total_size -= block->len; @@ -503,36 +445,19 @@ mem_heap_block_free( len = block->len; block->magic_n = MEM_FREED_BLOCK_MAGIC_N; -#ifndef UNIV_HOTBACKUP - if (!srv_use_sys_malloc) { -#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 */ + UNIV_MEM_ASSERT_W(block, len); - } +#ifndef UNIV_HOTBACKUP if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) { ut_ad(!buf_block); - mem_area_free(block, mem_comm_pool); + ut_free(block); } else { ut_ad(type & MEM_HEAP_BUFFER); buf_block_free(buf_block); } #else /* !UNIV_HOTBACKUP */ -#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 */ ut_free(block); #endif /* !UNIV_HOTBACKUP */ } @@ -540,7 +465,6 @@ mem_heap_block_free( #ifndef UNIV_HOTBACKUP /******************************************************************//** Frees the free_block field from a memory heap. */ -UNIV_INTERN void mem_heap_free_block_free( /*=====================*/ @@ -554,30 +478,3 @@ mem_heap_free_block_free( } } #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; - - mutex_enter(&(mem_comm_pool->mutex)); - - 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); - } - - mutex_exit(&(mem_comm_pool->mutex)); -} -#endif diff --git a/storage/innobase/mem/mem0pool.cc b/storage/innobase/mem/mem0pool.cc deleted file mode 100644 index fe9a84d21fa..00000000000 --- a/storage/innobase/mem/mem0pool.cc +++ /dev/null @@ -1,727 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1997, 2011, Oracle and/or its affiliates. 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., -51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/********************************************************************//** -@file mem/mem0pool.cc -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" -#include "srv0start.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_t{ - byte* buf; /*!< memory pool */ - ulint size; /*!< memory common pool size */ - ulint reserved; /*!< amount of currently allocated - memory */ - ib_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; - -#ifdef UNIV_PFS_MUTEX -/* Key to register mutex in mem_pool_t with performance schema */ -UNIV_INTERN mysql_pfs_key_t mem_pool_mutex_key; -#endif /* UNIV_PFS_MUTEX */ - -/* 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 if we are not in server shutdown. Use -this function only in memory free functions, since only memory -free functions are used during server shutdown. */ -UNIV_INLINE -void -mem_pool_mutex_enter( -/*=================*/ - mem_pool_t* pool) /*!< in: memory pool */ -{ - if (srv_shutdown_state < SRV_SHUTDOWN_EXIT_THREADS) { - mutex_enter(&(pool->mutex)); - } -} - -/********************************************************************//** -Releases the mem pool mutex if we are not in server shutdown. As -its corresponding mem_pool_mutex_enter() function, use it only -in memory free functions */ -UNIV_INLINE -void -mem_pool_mutex_exit( -/*================*/ - mem_pool_t* pool) /*!< in: memory pool */ -{ - if (srv_shutdown_state < SRV_SHUTDOWN_EXIT_THREADS) { - mutex_exit(&(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 = static_cast<mem_pool_t*>(ut_malloc(sizeof(mem_pool_t))); - - pool->buf = static_cast<byte*>(ut_malloc_low(size, TRUE)); - pool->size = size; - - mutex_create(mem_pool_mutex_key, &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); -} - -/********************************************************************//** -Frees a memory pool. */ -UNIV_INTERN -void -mem_pool_free( -/*==========*/ - mem_pool_t* pool) /*!< in, own: memory pool */ -{ - ut_free(pool->buf); - ut_free(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); - - mem_pool_mutex_enter(pool); - 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--; - mem_pool_mutex_exit(pool); - - 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--; - mem_pool_mutex_exit(pool); - - 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; - - mem_pool_mutex_enter(pool); - - free = 0; - - for (i = 0; i < 64; i++) { - - UT_LIST_CHECK(free_list, mem_area_t, pool->free_list[i]); - - for (area = UT_LIST_GET_FIRST(pool->free_list[i]); - area != 0; - area = UT_LIST_GET_NEXT(free_list, area)) { - - 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))); - - free += ut_2_exp(i); - } - } - - ut_a(free + pool->reserved == pool->size); - - mem_pool_mutex_exit(pool); - - 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); -} |