/******************************************************************//** 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 */ }
void mem_heap_block_free( /*================*/ mem_heap_t* heap, /* in: heap */ mem_block_t* block) /* in: block to free */ { ulint type; ulint len; ibool init_block; if (block->magic_n != MEM_BLOCK_MAGIC_N) { mem_analyze_corruption((byte*)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; init_block = block->init_block; 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); #endif if (init_block) { /* Do not have to free: do nothing */ } else if (type == MEM_HEAP_DYNAMIC) { mem_area_free(block, mem_comm_pool); } else { ut_ad(type & MEM_HEAP_BUFFER); if (len >= UNIV_PAGE_SIZE / 2) { buf_frame_free((byte*)block); } else { mem_area_free(block, mem_comm_pool); } } }
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((byte*)block); } block = UT_LIST_GET_NEXT(mem_block_list, block); } mem_pool_mutex_exit(); }
/******************************************************************//** 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)); }
/********************************************************************//** 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)); }
/********************************************************************//** 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 [email protected] 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))); }
/********************************************************************//** 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); }
mem_block_t* mem_heap_create_block( /*==================*/ /* out, own: memory heap block, NULL if did not succeed */ 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, or if init_block is not NULL, its size in bytes */ void* init_block, /* in: init block in fast create, type must be MEM_HEAP_DYNAMIC */ 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 */ { 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((byte*)heap); } /* In dynamic allocation, calculate the size: block header + data. */ if (init_block != NULL) { ut_ad(type == MEM_HEAP_DYNAMIC); ut_ad(n > MEM_BLOCK_START_SIZE + MEM_BLOCK_HEADER_SIZE); len = n; block = init_block; } else if (type == MEM_HEAP_DYNAMIC) { len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n); block = mem_area_alloc(len, mem_comm_pool); } else { ut_ad(n <= MEM_MAX_ALLOC_IN_BUF); len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n); if (len < UNIV_PAGE_SIZE / 2) { 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 */ block = (mem_block_t*)heap->free_block; heap->free_block = NULL; } else { block = (mem_block_t*)buf_frame_alloc(); } } } if (block == NULL) { return(NULL); } 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); block->free_block = NULL; block->init_block = (init_block != NULL); ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len); return(block); }
/***************************************************************//** 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(NULL, 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); if (UNIV_UNLIKELY(heap == NULL)) { /* This is the first block of the heap. The field total_size should be initialized here */ block->total_size = len; } else { /* Not the first allocation for the heap. This block's total_length field should be set to undefined. */ ut_d(block->total_size = ULINT_UNDEFINED); UNIV_MEM_INVALID(&block->total_size, sizeof block->total_size); heap->total_size += len; } ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len); return(block); }