diff options
Diffstat (limited to 'innobase/mem')
-rw-r--r-- | innobase/mem/Makefile.am | 24 | ||||
-rw-r--r-- | innobase/mem/makefilewin | 10 | ||||
-rw-r--r-- | innobase/mem/mem0dbg.c | 834 | ||||
-rw-r--r-- | innobase/mem/mem0mem.c | 298 | ||||
-rw-r--r-- | innobase/mem/mem0pool.c | 578 | ||||
-rw-r--r-- | innobase/mem/ts/makefile | 12 | ||||
-rw-r--r-- | innobase/mem/ts/tsmem.c | 497 |
7 files changed, 2253 insertions, 0 deletions
diff --git a/innobase/mem/Makefile.am b/innobase/mem/Makefile.am new file mode 100644 index 00000000000..89076f76f3b --- /dev/null +++ b/innobase/mem/Makefile.am @@ -0,0 +1,24 @@ +# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +# & Innobase Oy +# +# 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; either version 2 of the License, or +# (at your option) any later version. +# +# 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 + +include ../include/Makefile.i + +libs_LIBRARIES = libmem.a + +libmem_a_SOURCES = mem0mem.c mem0pool.c + +EXTRA_PROGRAMS = diff --git a/innobase/mem/makefilewin b/innobase/mem/makefilewin new file mode 100644 index 00000000000..8a30f8a6e71 --- /dev/null +++ b/innobase/mem/makefilewin @@ -0,0 +1,10 @@ +include ..\include\makefile.i + +mem.lib: mem0mem.obj mem0pool.obj makefile + lib -out:..\libs\mem.lib mem0mem.obj mem0pool.obj + +mem0mem.obj: mem0mem.c mem0dbg.c + $(CCOM) $(CFL) -c mem0mem.c + +mem0pool.obj: mem0pool.c + $(CCOM) $(CFL) -c mem0pool.c diff --git a/innobase/mem/mem0dbg.c b/innobase/mem/mem0dbg.c new file mode 100644 index 00000000000..0d71708b906 --- /dev/null +++ b/innobase/mem/mem0dbg.c @@ -0,0 +1,834 @@ +/************************************************************************ +The memory management: the debug code. This is not a compilation module, +but is included in mem0mem.* ! + +(c) 1994, 1995 Innobase Oy + +Created 6/9/1994 Heikki Tuuri +*************************************************************************/ + +mutex_t mem_hash_mutex; /* The mutex which protects in the + debug version the hash table containing + the list of live memory heaps, and + also the global variables below. */ + +/* The following variables contain information about the +extent of memory allocations. Only used in the debug version. +Protected by mem_hash_mutex above. */ + +ulint mem_n_created_heaps = 0; +ulint mem_n_allocations = 0; +ulint mem_total_allocated_memory = 0; +ulint mem_current_allocated_memory = 0; +ulint mem_max_allocated_memory = 0; +ulint mem_last_print_info = 0; + +/* 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 */ + 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 */ +mem_hash_cell_t mem_hash_table[MEM_HASH_SIZE]; + +/* The base node of the list of all allocated heaps */ +mem_hash_cell_t mem_all_list_base; + +ibool mem_hash_initialized = FALSE; + + +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 */ + +void +mem_field_header_set_len(byte* field, ulint len) +{ + ut_ad(len >= 0); + + mach_write(field - 2 * sizeof(ulint), len); +} + +ulint +mem_field_header_get_len(byte* field) +{ + return(mach_read(field - 2 * sizeof(ulint))); +} + +void +mem_field_header_set_check(byte* field, ulint check) +{ + mach_write(field - sizeof(ulint), check); +} + +ulint +mem_field_header_get_check(byte* field) +{ + return(mach_read(field - sizeof(ulint))); +} + +void +mem_field_trailer_set_check(byte* field, ulint check) +{ + mach_write(field + mem_field_header_get_len(field), check); +} + +ulint +mem_field_trailer_get_check(byte* field) +{ + return(mach_read(field + + mem_field_header_get_len(field))); +} + +/********************************************************************** +Initializes the memory system. */ + +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); + mutex_set_level(&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 + + mem_comm_pool = mem_pool_create(size); +} + +/********************************************************************** +Initializes an allocated memory field in the debug version. */ + +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. */ + +void +mem_field_erase( +/*============*/ + byte* buf, /* in: memory field */ + ulint n) /* 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. */ + +void +mem_init_buf( +/*=========*/ + byte* buf, /* in: pointer to buffer */ + ulint n) /* in: length of buffer */ +{ + byte* ptr; + + for (ptr = buf; ptr < buf + n; ptr++) { + + if (ut_rnd_gen_ibool()) { + *ptr = 0xBA; + } else { + *ptr = 0xBE; + } + } +} + +/******************************************************************* +Initializes a buffer to a random combination of hex DE and AD. +Used to erase freed memory.*/ + +void +mem_erase_buf( +/*==========*/ + byte* buf, /* in: pointer to buffer */ + ulint n) /* in: length of buffer */ +{ + byte* ptr; + + for (ptr = buf; ptr < buf + n; ptr++) { + if (ut_rnd_gen_ibool()) { + *ptr = 0xDE; + } else { + *ptr = 0xAD; + } + } +} + +/******************************************************************* +Inserts a created memory heap to the hash table of current allocated +memory heaps. */ + +void +mem_hash_insert( +/*============*/ + mem_heap_t* heap, /* in: the created heap */ + 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. */ + +void +mem_hash_remove( +/*============*/ + mem_heap_t* heap, /* in: the heap to be freed */ + 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) { + printf( + "Memory heap or buffer freed in %s line %lu did not exist.\n", + file_name, 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) { + printf("Inconsistency in memory heap or buffer n:o %lu created\n", + node->nth_heap); + printf("in %s line %lu and tried to free in %s line %lu.\n", + node->file_name, node->line, file_name, line); + ut_error; + } + + /* Free the memory occupied by the node struct */ + ut_free(node); + + mem_current_allocated_memory -= size; + + mutex_exit(&mem_hash_mutex); +} + +/******************************************************************* +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. */ + +void +mem_heap_validate_or_print( +/*=======================*/ + mem_heap_t* heap, /* in: memory heap */ + byte* top, /* 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) { + printf("Memory heap:"); + } + + while (block != NULL) { + phys_len += mem_block_get_len(block); + + if ((block->type == MEM_HEAP_BUFFER) + && (mem_block_get_len(block) > UNIV_PAGE_SIZE)) { + + /* error */ + + return; + } + + #ifdef UNIV_MEM_DEBUG + /* We can trace the fields of the block only in the debug + version */ + if (print) { + printf(" 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(user_field, len); + } + + total_len += len; + check_field = mem_field_header_get_check(user_field); + + if (check_field != + mem_field_trailer_get_check(user_field)) { + /* error */ + + 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 */ + + 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. */ + +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); + printf( + "\nheap type: %lu; size: user size %lu; physical size %lu; blocks %lu.\n", + heap->type, us_size, phys_size, n_blocks); + ut_a(!error); +} + +/****************************************************************** +Checks that an object is a memory heap (or a block of it). */ + +ibool +mem_heap_check( +/*===========*/ + /* out: TRUE if ok */ + mem_heap_t* heap) /* in: memory heap */ +{ + ut_a(heap->magic_n == MEM_BLOCK_MAGIC_N); + + return(TRUE); +} + +/****************************************************************** +Validates the contents of a memory heap. */ + +ibool +mem_heap_validate( +/*==============*/ + /* out: TRUE if ok */ + 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); + ut_a(!error); + + return(TRUE); +} + +/********************************************************************* +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 + + 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. */ + +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. */ + +void +mem_print_new_info(void) +/*====================*/ +{ + mem_print_info_low(FALSE); +} + +/********************************************************************* +TRUE if no memory is currently allocated. */ + +ibool +mem_all_freed(void) +/*===============*/ + /* out: TRUE if no heaps exist */ +{ + #ifdef UNIV_MEM_DEBUG + + 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) { + + ut_a(mem_pool_get_reserved(mem_comm_pool) == 0); + + return(TRUE); + } else { + return(FALSE); + } + + #else + + printf( + "Sorry, non-debug version cannot check if all memory is freed.\n"); + + return(FALSE); + + #endif +} + +/********************************************************************* +Validates the dynamic memory allocation system. */ + +ibool +mem_validate_no_assert(void) +/*========================*/ + /* out: TRUE if error */ +{ + #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 = FALSE; + ulint n_blocks; + ulint i; + + mem_pool_validate(mem_comm_pool); + + 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) { + printf("\nERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n"); + printf("Inconsistency in memory heap or buffer created\n"); + printf("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); + + #else + + printf("Sorry, non-debug version cannot validate dynamic memory\n"); + + return(FALSE); + + #endif +} + +/**************************************************************** +Validates the dynamic memory */ + +ibool +mem_validate(void) +/*==============*/ + /* out: TRUE if ok */ +{ + ut_a(!mem_validate_no_assert()); + + return(TRUE); +} diff --git a/innobase/mem/mem0mem.c b/innobase/mem/mem0mem.c new file mode 100644 index 00000000000..19a2c0d61a7 --- /dev/null +++ b/innobase/mem/mem0mem.c @@ -0,0 +1,298 @@ +/************************************************************************ +The memory management + +(c) 1994, 1995 Innobase Oy + +Created 6/9/1994 Heikki Tuuri +*************************************************************************/ + + +#include "mem0mem.h" +#ifdef UNIV_NONINL +#include "mem0mem.ic" +#endif + +#include "mach0data.h" +#include "buf0buf.h" +#include "mem0dbg.c" +#include "btr0sea.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. */ + +/******************************************************************* +NOTE: Use the corresponding macro instead of this function. +Allocates a single buffer of memory from the dynamic memory of +the C compiler. Is like malloc of C. The buffer must be freed +with mem_free. */ + +void* +mem_alloc_func_noninline( +/*=====================*/ + /* out, own: free storage, NULL if did not + succeed */ + ulint n /* in: desired number of bytes */ + #ifdef UNIV_MEM_DEBUG + ,char* file_name, /* in: file name where created */ + ulint line /* in: line where created */ + #endif + ) +{ + return(mem_alloc_func(n +#ifdef UNIV_MEM_DEBUG + , file_name, line +#endif + )); +} + +/******************************************************************* +Creates a memory heap block where data can be allocated. */ + +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 possibly ORed to MEM_HEAP_BTR_SEARCH */ +{ + mem_block_t* block; + ulint len; + + ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER) + || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH)); + + /* 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; + + 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; + + if (init_block != NULL) { + block->init_block = TRUE; + } else { + block->init_block = FALSE; + } + + ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len); + + return(block); +} + +/******************************************************************* +Adds a new block to a memory heap. */ + +mem_block_t* +mem_heap_add_block( +/*===============*/ + /* out: created block, NULL if did not + succeed */ + 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) { + ut_ad(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, NULL, heap->type); + + 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. */ + +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; + + UT_LIST_REMOVE(list, heap->base, block); + + type = heap->type; + len = block->len; + init_block = block->init_block; + + #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); + } + } +} + +/********************************************************************** +Frees the free_block field from a memory heap. */ + +void +mem_heap_free_block_free( +/*=====================*/ + mem_heap_t* heap) /* in: heap */ +{ + if (heap->free_block) { + + buf_frame_free(heap->free_block); + + heap->free_block = NULL; + } +} diff --git a/innobase/mem/mem0pool.c b/innobase/mem/mem0pool.c new file mode 100644 index 00000000000..7418ee36dbc --- /dev/null +++ b/innobase/mem/mem0pool.c @@ -0,0 +1,578 @@ +/************************************************************************ +The lowest-level memory management + +(c) 1997 Innobase Oy + +Created 5/12/1997 Heikki Tuuri +*************************************************************************/ + +#include "mem0pool.h" +#ifdef UNIV_NONINL +#include "mem0pool.ic" +#endif + +#include "sync0sync.h" +#include "ut0mem.h" +#include "ut0lst.h" +#include "ut0byte.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-3 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. + +So, let the SQL statements and the data dictionary entries form one single +LRU list, let us call it the dictionary LRU list. 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 truncate the LRU list of +the dictionary cache. 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. */ + +/* Memory area header */ + +struct mem_area_struct{ + ulint size_and_free; /* memory area size is obtained by + anding with ~MEM_AREA_FREE; area in + a free list if ANDing with + MEM_AREA_FREE results in nonzero */ + UT_LIST_NODE_T(mem_area_t) + free_list; /* free list node */ +}; + +/* 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 * UNIV_MEM_ALIGNMENT) + +/* 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 */ +mem_pool_t* mem_comm_pool = NULL; + +ulint mem_out_of_mem_err_msg_count = 0; + +/************************************************************************ +Returns memory area size. */ +UNIV_INLINE +ulint +mem_area_get_size( +/*==============*/ + /* out: 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. */ +UNIV_INLINE +ibool +mem_area_get_free( +/*==============*/ + /* out: TRUE if free */ + mem_area_t* area) /* in: area */ +{ + ut_ad(TRUE == MEM_AREA_FREE); + + 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 */ +{ + ut_ad(TRUE == MEM_AREA_FREE); + + area->size_and_free = (area->size_and_free & ~MEM_AREA_FREE) + | free; +} + +/************************************************************************ +Creates a memory pool. */ + +mem_pool_t* +mem_pool_create( +/*============*/ + /* out: memory pool */ + ulint size) /* in: pool size in bytes */ +{ + mem_pool_t* pool; + mem_area_t* area; + ulint i; + ulint used; + + ut_a(size > 10000); + + pool = ut_malloc(sizeof(mem_pool_t)); + + pool->buf = ut_malloc(size); + pool->size = size; + + mutex_create(&(pool->mutex)); + mutex_set_level(&(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); + + 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. */ +static +ibool +mem_pool_fill_free_list( +/*====================*/ + /* out: TRUE if we were able to insert a + block to the 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 (i >= 63) { + /* We come here when we have run out of space in the + memory pool: */ + + if (mem_out_of_mem_err_msg_count % 1000 == 0) { + /* We do not print the message every time: */ + + fprintf(stderr, + "Innobase: Warning: out of memory in additional memory pool.\n"); + fprintf(stderr, + "Innobase: Innobase will start allocating memory from the OS.\n"); + fprintf(stderr, + "Innobase: You should restart the database with a bigger value in\n"); + fprintf(stderr, + "Innobase: the MySQL .cnf file for innobase_additional_mem_pool_size.\n"); + } + + mem_out_of_mem_err_msg_count++; + + return(FALSE); + } + + area = UT_LIST_GET_FIRST(pool->free_list[i + 1]); + + if (area == NULL) { + ret = mem_pool_fill_free_list(i + 1, pool); + + if (ret == FALSE) { + return(FALSE); + } + + area = UT_LIST_GET_FIRST(pool->free_list[i + 1]); + } + + UT_LIST_REMOVE(free_list, pool->free_list[i + 1], area); + + area2 = (mem_area_t*)(((byte*)area) + ut_2_exp(i)); + + 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.*! */ + +void* +mem_area_alloc( +/*===========*/ + /* out, own: allocated memory buffer */ + ulint size, /* in: allocated size in bytes; for optimum + space usage, the size should be a power of 2 + minus MEM_AREA_EXTRA_SIZE */ + mem_pool_t* pool) /* in: memory pool */ +{ + mem_area_t* area; + ulint n; + ibool ret; + + n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE)); + + mutex_enter(&(pool->mutex)); + + 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: */ + + mutex_exit(&(pool->mutex)); + + return(ut_malloc(size)); + } + + area = UT_LIST_GET_FIRST(pool->free_list[n]); + } + + ut_a(mem_area_get_free(area)); + 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); + + mutex_exit(&(pool->mutex)); + + ut_ad(mem_pool_validate(pool)); + + return((void*)(MEM_AREA_EXTRA_SIZE + ((byte*)area))); +} + +/************************************************************************ +Gets the buddy of an area, if it exists in pool. */ +UNIV_INLINE +mem_area_t* +mem_area_get_buddy( +/*===============*/ + /* out: the buddy, NULL if no buddy in pool */ + 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. */ + +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 (mem_out_of_mem_err_msg_count > 0) { + /* 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); + + size = mem_area_get_size(area); + + ut_ad(size != 0); + ut_a(!mem_area_get_free(area)); + +#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)); + ut_a(ut_2_power_up(next_size) == next_size); + } +#endif + buddy = mem_area_get_buddy(area, size, pool); + + n = ut_2_log(size); + + mutex_enter(&(pool->mutex)); + + 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); + + 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; + } + + mutex_exit(&(pool->mutex)); + + ut_ad(mem_pool_validate(pool)); +} + +/************************************************************************ +Validates a memory pool. */ + +ibool +mem_pool_validate( +/*==============*/ + /* out: TRUE if ok */ + 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]); + + 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 + - (pool->size % MEM_AREA_MIN_SIZE)); + mutex_exit(&(pool->mutex)); + + return(TRUE); +} + +/************************************************************************ +Prints info of a memory pool. */ + +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", + UT_LIST_GET_LEN(pool->free_list[i]), + ut_2_exp(i)); + } + } + + fprintf(outfile, "Pool size %lu, reserved %lu.\n", pool->size, + pool->reserved); + mutex_exit(&(pool->mutex)); +} + +/************************************************************************ +Returns the amount of reserved memory. */ + +ulint +mem_pool_get_reserved( +/*==================*/ + /* out: reserved mmeory in bytes */ + mem_pool_t* pool) /* in: memory pool */ +{ + ulint reserved; + + mutex_enter(&(pool->mutex)); + + reserved = pool->reserved; + + mutex_exit(&(pool->mutex)); + + return(reserved); +} diff --git a/innobase/mem/ts/makefile b/innobase/mem/ts/makefile new file mode 100644 index 00000000000..0f6855322ce --- /dev/null +++ b/innobase/mem/ts/makefile @@ -0,0 +1,12 @@ + +include ..\..\makefile.i + +tsmem: ..\mem.lib tsmem.c makefile + $(CCOM) $(CFL) -I.. -I..\.. ..\mem.lib ..\..\btr.lib ..\..\trx.lib ..\..\pars.lib ..\..\que.lib ..\..\lock.lib ..\..\row.lib ..\..\read.lib ..\..\srv.lib ..\..\com.lib ..\..\usr.lib ..\..\thr.lib ..\..\fut.lib ..\..\fsp.lib ..\..\page.lib ..\..\dyn.lib ..\..\mtr.lib ..\..\log.lib ..\..\rem.lib ..\..\fil.lib ..\..\buf.lib ..\..\dict.lib ..\..\data.lib ..\..\mach.lib ..\..\ha.lib ..\..\ut.lib ..\..\sync.lib ..\..\os.lib tsmem.c $(LFL) + + + + + + + diff --git a/innobase/mem/ts/tsmem.c b/innobase/mem/ts/tsmem.c new file mode 100644 index 00000000000..4a108251673 --- /dev/null +++ b/innobase/mem/ts/tsmem.c @@ -0,0 +1,497 @@ +/************************************************************************ +The test module for the memory management of Innobase + +(c) 1994, 1995 Innobase Oy + +Created 6/10/1994 Heikki Tuuri +*************************************************************************/ + +#include "../mem0mem.h" +#include "sync0sync.h" +#include "ut0rnd.h" + +mem_heap_t* heap_arr[1200]; + +byte* buf_arr[10000]; +ulint rnd_arr[10000]; + + +#ifdef UNIV_DEBUG +/********************************************************************* +Debug version test. */ + +void +test1(void) +/*=======*/ +{ + mem_heap_t* heap_1, *heap_2; + byte* buf_1, *buf_2, *buf_3; + byte check; + bool error; + ulint i; + ulint j; + ulint sum; + ulint user_size; + ulint phys_size, phys_size_1, phys_size_2; + ulint n_blocks; + ulint p; + byte block[1024]; + byte* top_1, *top_2; + + /* For this test to work the memory alignment must be + even (presumably a reasonable assumption) */ + ut_a(0 == (UNIV_MEM_ALIGNMENT & 1)); + + printf("-------------------------------------------\n"); + printf("TEST 1. Basic test \n"); + + heap_1 = mem_heap_create(0); + + buf_1 = mem_heap_alloc(heap_1, 11); + + heap_2 = mem_heap_create(0); + + buf_2 = mem_heap_alloc(heap_1, 15); + + /* Test that the field is properly initialized */ + for (i = 0; i < 11; i++) { + ut_a((*(buf_1 + i) == 0xBA) || (*(buf_1 + i) == 0xBE)); + } + + check = *(buf_1 + 11); + + mem_validate(); + + /* Make an advertent error in the heap */ + (*(buf_1 + 11))++; + + error = mem_validate_no_assert(); + + ut_a(error); + + /* Fix the error in heap before freeing */ + *(buf_1 + 11) = check; + + mem_print_info(); + + /* Free the top buffer buf_2 */ + mem_heap_free_top(heap_1, 15); + + /* Test that the field is properly erased */ + for (i = 0; i < 15; i++) { + ut_a((*(buf_2 + i) == 0xDE) || (*(buf_2 + i) == 0xAD)); + } + + /* Test that a new buffer is allocated from the same position + as buf_2 */ + buf_3 = mem_heap_alloc(heap_1, 15); + + ut_a(buf_3 == buf_2); + + mem_heap_free(heap_1); + + /* Test that the field is properly erased */ + for (i = 0; i < 11; i++) { + ut_a((*(buf_1 + i) == 0xDE) || (*(buf_1 + i) == 0xAD)); + } + + mem_validate(); + + mem_print_info(); + + printf("-------------------------------------------\n"); + printf("TEST 2. Test of massive allocation and freeing\n"); + + sum = 0; + for (i = 0; i < 10000; i++) { + + j = ut_rnd_gen_ulint() % 16 + 15; + + sum = sum + j; + + buf_1 = mem_heap_alloc(heap_2, j); + rnd_arr[i] = j; + + buf_arr[i] = buf_1; + + ut_a(buf_1 == mem_heap_get_top(heap_2, j)); + } + + mem_heap_validate_or_print(heap_2, NULL, FALSE, &error, &user_size, + &phys_size_1, + &n_blocks); + + ut_a(!error); + ut_a(user_size == sum); + + (*(buf_1 - 1))++; + + ut_a(mem_validate_no_assert()); + + (*(buf_1 - 1))--; + + mem_print_info(); + + + for (p = 10000; p > 0 ; p--) { + + j = rnd_arr[p - 1]; + + ut_a(buf_arr[p - 1] == mem_heap_get_top(heap_2, j)); + mem_heap_free_top(heap_2, j); + } + + mem_print_info(); + + mem_heap_free(heap_2); + + mem_print_info(); + + printf("-------------------------------------------\n"); + printf("TEST 3. More tests on the validating \n"); + + heap_1 = mem_heap_create(UNIV_MEM_ALIGNMENT * 20); + + buf_1 = mem_heap_alloc(heap_1, UNIV_MEM_ALIGNMENT * 20); + + mem_heap_validate_or_print(heap_1, NULL, FALSE, &error, &user_size, + &phys_size_1, + &n_blocks); + + ut_a((ulint)(buf_1 - (byte*)heap_1) == (MEM_BLOCK_HEADER_SIZE + + MEM_FIELD_HEADER_SIZE)); + + mem_validate(); + + mem_print_info(); + + ut_a(user_size == UNIV_MEM_ALIGNMENT * 20); + ut_a(phys_size_1 == (ulint)(ut_calc_align(MEM_FIELD_HEADER_SIZE + + UNIV_MEM_ALIGNMENT * 20 + + MEM_FIELD_TRAILER_SIZE, + UNIV_MEM_ALIGNMENT) + + MEM_BLOCK_HEADER_SIZE)); + + ut_a(n_blocks == 1); + + buf_2 = mem_heap_alloc(heap_1, UNIV_MEM_ALIGNMENT * 3 - 1); + + mem_heap_validate_or_print(heap_1, NULL, FALSE, &error, + &user_size, &phys_size_2, + &n_blocks); + + printf("Physical size of the heap %ld\n", phys_size_2); + + ut_a(!error); + ut_a(user_size == UNIV_MEM_ALIGNMENT * 23 - 1); + ut_a(phys_size_2 == (ulint) (phys_size_1 + + ut_calc_align(MEM_FIELD_HEADER_SIZE + + phys_size_1 * 2 + + MEM_FIELD_TRAILER_SIZE, + UNIV_MEM_ALIGNMENT) + + MEM_BLOCK_HEADER_SIZE)); + + ut_a(n_blocks == 2); + + buf_3 = mem_heap_alloc(heap_1, UNIV_MEM_ALIGNMENT * 3 + 5); + + ut_a((ulint)(buf_3 - buf_2) == ut_calc_align( + (UNIV_MEM_ALIGNMENT * 3 + + MEM_FIELD_TRAILER_SIZE), + UNIV_MEM_ALIGNMENT) + + MEM_FIELD_HEADER_SIZE); + + + ut_memcpy(buf_3, buf_2, UNIV_MEM_ALIGNMENT * 3); + + mem_heap_validate_or_print(heap_1, NULL, FALSE, &error, + &user_size, &phys_size, + &n_blocks); + + ut_a(!error); + ut_a(user_size == UNIV_MEM_ALIGNMENT * 26 + 4); + ut_a(phys_size == phys_size_2); + ut_a(n_blocks == 2); + + + /* Make an advertent error to buf_3 */ + + (*(buf_3 - 1))++; + + mem_heap_validate_or_print(heap_1, NULL, FALSE, &error, + &user_size, &phys_size, + &n_blocks); + + ut_a(error); + ut_a(user_size == 0); + ut_a(phys_size == 0); + ut_a(n_blocks == 0); + + /* Fix the error and make another */ + + (*(buf_3 - 1))--; + (*(buf_3 + UNIV_MEM_ALIGNMENT * 3 + 5))++; + + mem_heap_validate_or_print(heap_1, NULL, FALSE, &error, + &user_size, &phys_size, + &n_blocks); + + ut_a(error); + + (*(buf_3 + UNIV_MEM_ALIGNMENT * 3 + 5))--; + + buf_1 = mem_heap_alloc(heap_1, UNIV_MEM_ALIGNMENT + 4); + + ut_a((ulint)(buf_1 - buf_3) == ut_calc_align(UNIV_MEM_ALIGNMENT * 3 + 5 + + MEM_FIELD_TRAILER_SIZE , + UNIV_MEM_ALIGNMENT) + + MEM_FIELD_HEADER_SIZE); + + + mem_heap_validate_or_print(heap_1, NULL, FALSE, &error, + &user_size, &phys_size, + &n_blocks); + + ut_a(!error); + ut_a(user_size == UNIV_MEM_ALIGNMENT * 27 + 8); + ut_a(phys_size == phys_size_2); + ut_a(n_blocks == 2); + + + mem_print_info(); + + mem_heap_free(heap_1); + + printf("-------------------------------------------\n"); + printf("TEST 4. Test of massive allocation \n"); + printf("of heaps to test the hash table\n"); + + for (i = 0; i < 500; i++) { + heap_arr[i] = mem_heap_create(i); + buf_2 = mem_heap_alloc(heap_arr[i], 2 * i); + } + + mem_validate(); + + for (i = 0; i < 500; i++) { + mem_heap_free(heap_arr[i]); + } + + mem_validate(); + + mem_print_info(); + + /* Validating a freed heap should generate an error */ + + mem_heap_validate_or_print(heap_1, NULL, FALSE, &error, + NULL, NULL, NULL); + + ut_a(error); + + printf("-------------------------------------------\n"); + printf("TEST 5. Test of mem_alloc and mem_free \n"); + + buf_1 = mem_alloc(11100); + buf_2 = mem_alloc(23); + + ut_memcpy(buf_2, buf_1, 23); + + mem_validate(); + + mem_print_info(); + + mem_free(buf_1); + mem_free(buf_2); + + mem_validate(); + + printf("-------------------------------------------\n"); + printf("TEST 6. Test of mem_heap_print \n"); + + heap_1 = mem_heap_create(0); + + buf_1 = mem_heap_alloc(heap_1, 7); + + ut_memcpy(buf_1, "Pascal", 7); + + for (i = 0; i < 10; i++) { + buf_1 = mem_heap_alloc(heap_1, 6); + ut_memcpy(buf_1, "Cobol", 6); + } + + printf("A heap with 1 Pascal and 10 Cobol's\n"); + mem_heap_print(heap_1); + + for (i = 0; i < 10; i++) { + mem_heap_free_top(heap_1, 6); + } + + printf("A heap with 1 Pascal and 0 Cobol's\n"); + mem_heap_print(heap_1); + + ut_a(mem_all_freed() == FALSE); + + mem_heap_free(heap_1); + + ut_a(mem_all_freed() == TRUE); + + mem_print_info(); + + printf("-------------------------------------------\n"); + printf("TEST 7. Test of mem_heap_fast_create \n"); + + heap_1 = mem_heap_fast_create(1024, block); + + buf_1 = mem_heap_alloc(heap_1, 7); + + ut_memcpy(buf_1, "Pascal", 7); + + for (i = 0; i < 1000; i++) { + buf_1 = mem_heap_alloc(heap_1, 6); + ut_memcpy(buf_1, "Cobol", 6); + } + + for (i = 0; i < 1000; i++) { + mem_heap_free_top(heap_1, 6); + } + + ut_a(mem_all_freed() == FALSE); + + mem_heap_free(heap_1); + + ut_a(mem_all_freed() == TRUE); + + mem_print_info(); + + printf("-------------------------------------------\n"); + printf("TEST 8. Test of heap top freeing \n"); + + heap_1 = mem_heap_fast_create(1024, block); + + top_1 = mem_heap_get_heap_top(heap_1); + + buf_1 = mem_heap_alloc(heap_1, 7); + + ut_memcpy(buf_1, "Pascal", 7); + + for (i = 0; i < 500; i++) { + buf_1 = mem_heap_alloc(heap_1, 6); + ut_memcpy(buf_1, "Cobol", 6); + } + + top_2 = mem_heap_get_heap_top(heap_1); + + for (i = 0; i < 500; i++) { + buf_1 = mem_heap_alloc(heap_1, 6); + ut_memcpy(buf_1, "Cobol", 6); + } + + mem_heap_free_heap_top(heap_1, top_2); + + mem_heap_free_heap_top(heap_1, top_1); + + ut_a(mem_all_freed() == FALSE); + + for (i = 0; i < 500; i++) { + buf_1 = mem_heap_alloc(heap_1, 6); + ut_memcpy(buf_1, "Cobol", 6); + + } + + mem_heap_empty(heap_1); + + for (i = 0; i < 500; i++) { + buf_1 = mem_heap_alloc(heap_1, 6); + ut_memcpy(buf_1, "Cobol", 6); + + } + + mem_heap_free(heap_1); + + ut_a(mem_all_freed() == TRUE); + + mem_print_info(); + +} +#endif /* UNIV_DEBUG */ + +/**************************************************************** +Allocation speed test. */ + +void +test2(void) +/*=======*/ +{ + mem_heap_t* heap; + ulint tm, oldtm; + ulint i; + byte* buf; + byte block[512]; + + printf("-------------------------------------------\n"); + printf("TEST B1. Test of speed \n"); + + oldtm = ut_clock(); + + for (i = 0; i < 10000 * UNIV_DBC * UNIV_DBC; i++) { + heap = mem_heap_create(500); + mem_heap_free(heap); + } + + tm = ut_clock(); + + printf("Time for %ld heap create-free pairs %ld millisecs.\n", + i, tm - oldtm); + + + oldtm = ut_clock(); + + for (i = 0; i < 10000 * UNIV_DBC * UNIV_DBC; i++) { + heap = mem_heap_fast_create(512, block); + mem_heap_free(heap); + } + + tm = ut_clock(); + + printf("Time for %ld heap fast-create-free pairs %ld millisecs.\n", + i, tm - oldtm); + + + heap = mem_heap_create(500); + + oldtm = ut_clock(); + + for (i = 0; i < 10000 * UNIV_DBC * UNIV_DBC; i++) { + buf = mem_heap_alloc(heap, 50); + mem_heap_free_top(heap, 50); + } + + tm = ut_clock(); + + printf("Time for %ld heap alloc-free-top pairs %ld millisecs.\n", + i, tm - oldtm); + + mem_heap_free(heap); +} + + +void +main(void) +{ + sync_init(); + mem_init(2500000); + + #ifdef UNIV_DEBUG + + test1(); + + #endif + + test2(); + + ut_ad(sync_all_freed()); + + ut_ad(mem_all_freed()); + + printf("TEST SUCCESSFULLY COMPLETED!\n"); +} |