mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 14:33:32 +03:00 
			
		
		
		
	Detailed revision comments: r6446 | marko | 2010-01-13 17:20:10 +0200 (Wed, 13 Jan 2010) | 3 lines branches/zip: Treat mem_hash_mutex specially in mutex_free(), and explicitly free mem_hash_mutex in mem_close(). This fixes the breakage of UNIV_MEM_DEBUG that was filed as Issue #434.
		
			
				
	
	
		
			1042 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1042 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*****************************************************************************
 | 
						|
 | 
						|
Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved.
 | 
						|
 | 
						|
This program is free software; you can redistribute it and/or modify it under
 | 
						|
the terms of the GNU General Public License as published by the Free Software
 | 
						|
Foundation; version 2 of the License.
 | 
						|
 | 
						|
This program is distributed in the hope that it will be useful, but WITHOUT
 | 
						|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
						|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License along with
 | 
						|
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 | 
						|
Place, Suite 330, Boston, MA 02111-1307 USA
 | 
						|
 | 
						|
*****************************************************************************/
 | 
						|
 | 
						|
/********************************************************************//**
 | 
						|
@file mem/mem0dbg.c
 | 
						|
The memory management: the debug code. This is not a compilation module,
 | 
						|
but is included in mem0mem.* !
 | 
						|
 | 
						|
Created 6/9/1994 Heikki Tuuri
 | 
						|
*************************************************************************/
 | 
						|
 | 
						|
#ifdef UNIV_MEM_DEBUG
 | 
						|
# ifndef UNIV_HOTBACKUP
 | 
						|
/* The mutex which protects in the debug version the hash table
 | 
						|
containing the list of live memory heaps, and also the global
 | 
						|
variables below. */
 | 
						|
UNIV_INTERN mutex_t	mem_hash_mutex;
 | 
						|
# endif /* !UNIV_HOTBACKUP */
 | 
						|
 | 
						|
/* The following variables contain information about the
 | 
						|
extent of memory allocations. Only used in the debug version.
 | 
						|
Protected by mem_hash_mutex above. */
 | 
						|
 | 
						|
static ulint		mem_n_created_heaps		= 0;
 | 
						|
static ulint		mem_n_allocations		= 0;
 | 
						|
static ulint		mem_total_allocated_memory	= 0;
 | 
						|
UNIV_INTERN ulint	mem_current_allocated_memory	= 0;
 | 
						|
static ulint		mem_max_allocated_memory	= 0;
 | 
						|
# ifndef UNIV_HOTBACKUP
 | 
						|
static ulint		mem_last_print_info		= 0;
 | 
						|
static ibool		mem_hash_initialized		= FALSE;
 | 
						|
# endif /* !UNIV_HOTBACKUP */
 | 
						|
 | 
						|
/* Size of the hash table for memory management tracking */
 | 
						|
#define	MEM_HASH_SIZE	997
 | 
						|
 | 
						|
/* The node of the list containing currently allocated memory heaps */
 | 
						|
 | 
						|
typedef struct mem_hash_node_struct mem_hash_node_t;
 | 
						|
struct mem_hash_node_struct {
 | 
						|
	UT_LIST_NODE_T(mem_hash_node_t)
 | 
						|
				list;	/*!< hash list node */
 | 
						|
	mem_heap_t*		heap;	/*!< memory heap */
 | 
						|
	const char*		file_name;/* file where heap was created*/
 | 
						|
	ulint			line;	/*!< file line of creation */
 | 
						|
	ulint			nth_heap;/* this is the nth heap created */
 | 
						|
	UT_LIST_NODE_T(mem_hash_node_t)
 | 
						|
				all_list;/* list of all created heaps */
 | 
						|
};
 | 
						|
 | 
						|
typedef UT_LIST_BASE_NODE_T(mem_hash_node_t) mem_hash_cell_t;
 | 
						|
 | 
						|
/* The hash table of allocated heaps */
 | 
						|
static mem_hash_cell_t		mem_hash_table[MEM_HASH_SIZE];
 | 
						|
 | 
						|
/* The base node of the list of all allocated heaps */
 | 
						|
static mem_hash_cell_t		mem_all_list_base;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
UNIV_INLINE
 | 
						|
mem_hash_cell_t*
 | 
						|
mem_hash_get_nth_cell(ulint i);
 | 
						|
 | 
						|
/* Accessor function for the hash table. Returns a pointer to the
 | 
						|
table cell. */
 | 
						|
UNIV_INLINE
 | 
						|
mem_hash_cell_t*
 | 
						|
mem_hash_get_nth_cell(ulint i)
 | 
						|
{
 | 
						|
	ut_a(i < MEM_HASH_SIZE);
 | 
						|
 | 
						|
	return(&(mem_hash_table[i]));
 | 
						|
}
 | 
						|
 | 
						|
/* Accessor functions for a memory field in the debug version */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_field_header_set_len(byte* field, ulint len)
 | 
						|
{
 | 
						|
	mach_write_to_4(field - 2 * sizeof(ulint), len);
 | 
						|
}
 | 
						|
 | 
						|
UNIV_INTERN
 | 
						|
ulint
 | 
						|
mem_field_header_get_len(byte* field)
 | 
						|
{
 | 
						|
	return(mach_read_from_4(field - 2 * sizeof(ulint)));
 | 
						|
}
 | 
						|
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_field_header_set_check(byte* field, ulint check)
 | 
						|
{
 | 
						|
	mach_write_to_4(field - sizeof(ulint), check);
 | 
						|
}
 | 
						|
 | 
						|
UNIV_INTERN
 | 
						|
ulint
 | 
						|
mem_field_header_get_check(byte* field)
 | 
						|
{
 | 
						|
	return(mach_read_from_4(field - sizeof(ulint)));
 | 
						|
}
 | 
						|
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_field_trailer_set_check(byte* field, ulint check)
 | 
						|
{
 | 
						|
	mach_write_to_4(field + mem_field_header_get_len(field), check);
 | 
						|
}
 | 
						|
 | 
						|
UNIV_INTERN
 | 
						|
ulint
 | 
						|
mem_field_trailer_get_check(byte* field)
 | 
						|
{
 | 
						|
	return(mach_read_from_4(field
 | 
						|
				+ mem_field_header_get_len(field)));
 | 
						|
}
 | 
						|
#endif /* UNIV_MEM_DEBUG */
 | 
						|
 | 
						|
#ifndef UNIV_HOTBACKUP
 | 
						|
/******************************************************************//**
 | 
						|
Initializes the memory system. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_init(
 | 
						|
/*=====*/
 | 
						|
	ulint	size)	/*!< in: common pool size in bytes */
 | 
						|
{
 | 
						|
#ifdef UNIV_MEM_DEBUG
 | 
						|
 | 
						|
	ulint	i;
 | 
						|
 | 
						|
	/* Initialize the hash table */
 | 
						|
	ut_a(FALSE == mem_hash_initialized);
 | 
						|
 | 
						|
	mutex_create(&mem_hash_mutex, SYNC_MEM_HASH);
 | 
						|
 | 
						|
	for (i = 0; i < MEM_HASH_SIZE; i++) {
 | 
						|
		UT_LIST_INIT(*mem_hash_get_nth_cell(i));
 | 
						|
	}
 | 
						|
 | 
						|
	UT_LIST_INIT(mem_all_list_base);
 | 
						|
 | 
						|
	mem_hash_initialized = TRUE;
 | 
						|
#endif
 | 
						|
 | 
						|
	if (UNIV_LIKELY(srv_use_sys_malloc)) {
 | 
						|
		/* When innodb_use_sys_malloc is set, the
 | 
						|
		mem_comm_pool won't be used for any allocations.  We
 | 
						|
		create a dummy mem_comm_pool, because some statistics
 | 
						|
		and debugging code relies on it being initialized. */
 | 
						|
		size = 1;
 | 
						|
	}
 | 
						|
 | 
						|
	mem_comm_pool = mem_pool_create(size);
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************//**
 | 
						|
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 __attribute__((unused)))
 | 
						|
			/*!< in: how many bytes the user requested */
 | 
						|
{
 | 
						|
	byte*	usr_buf;
 | 
						|
 | 
						|
	usr_buf = buf + MEM_FIELD_HEADER_SIZE;
 | 
						|
 | 
						|
	mutex_enter(&mem_hash_mutex);
 | 
						|
	mem_current_allocated_memory	-= n;
 | 
						|
	mutex_exit(&mem_hash_mutex);
 | 
						|
 | 
						|
	/* Check that the field lengths agree */
 | 
						|
	ut_ad(n == (ulint)mem_field_header_get_len(usr_buf));
 | 
						|
 | 
						|
	/* In the debug version, set the freed space to a random
 | 
						|
	combination of 0xDE and 0xAD */
 | 
						|
 | 
						|
	mem_erase_buf(buf, MEM_SPACE_NEEDED(n));
 | 
						|
}
 | 
						|
 | 
						|
/***************************************************************//**
 | 
						|
Initializes a buffer to a random combination of hex BA and BE.
 | 
						|
Used to initialize allocated memory. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_init_buf(
 | 
						|
/*=========*/
 | 
						|
	byte*	buf,	/*!< in: pointer to buffer */
 | 
						|
	ulint	 n)	/*!< in: length of buffer */
 | 
						|
{
 | 
						|
	byte*	ptr;
 | 
						|
 | 
						|
	UNIV_MEM_ASSERT_W(buf, n);
 | 
						|
 | 
						|
	for (ptr = buf; ptr < buf + n; ptr++) {
 | 
						|
 | 
						|
		if (ut_rnd_gen_ibool()) {
 | 
						|
			*ptr = 0xBA;
 | 
						|
		} else {
 | 
						|
			*ptr = 0xBE;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	UNIV_MEM_INVALID(buf, n);
 | 
						|
}
 | 
						|
 | 
						|
/***************************************************************//**
 | 
						|
Initializes a buffer to a random combination of hex DE and AD.
 | 
						|
Used to erase freed memory. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_erase_buf(
 | 
						|
/*==========*/
 | 
						|
	byte*	buf,	/*!< in: pointer to buffer */
 | 
						|
	ulint	n)	/*!< in: length of buffer */
 | 
						|
{
 | 
						|
	byte*	ptr;
 | 
						|
 | 
						|
	UNIV_MEM_ASSERT_W(buf, n);
 | 
						|
 | 
						|
	for (ptr = buf; ptr < buf + n; ptr++) {
 | 
						|
		if (ut_rnd_gen_ibool()) {
 | 
						|
			*ptr = 0xDE;
 | 
						|
		} else {
 | 
						|
			*ptr = 0xAD;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	UNIV_MEM_FREE(buf, n);
 | 
						|
}
 | 
						|
 | 
						|
/***************************************************************//**
 | 
						|
Inserts a created memory heap to the hash table of current allocated
 | 
						|
memory heaps. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_hash_insert(
 | 
						|
/*============*/
 | 
						|
	mem_heap_t*	heap,	   /*!< in: the created heap */
 | 
						|
	const char*	file_name, /*!< in: file name of creation */
 | 
						|
	ulint		line)	   /*!< in: line where created */
 | 
						|
{
 | 
						|
	mem_hash_node_t*	new_node;
 | 
						|
	ulint			cell_no	;
 | 
						|
 | 
						|
	ut_ad(mem_heap_check(heap));
 | 
						|
 | 
						|
	mutex_enter(&mem_hash_mutex);
 | 
						|
 | 
						|
	cell_no = ut_hash_ulint((ulint)heap, MEM_HASH_SIZE);
 | 
						|
 | 
						|
	/* Allocate a new node to the list */
 | 
						|
	new_node = ut_malloc(sizeof(mem_hash_node_t));
 | 
						|
 | 
						|
	new_node->heap = heap;
 | 
						|
	new_node->file_name = file_name;
 | 
						|
	new_node->line = line;
 | 
						|
	new_node->nth_heap = mem_n_created_heaps;
 | 
						|
 | 
						|
	/* Insert into lists */
 | 
						|
	UT_LIST_ADD_FIRST(list, *mem_hash_get_nth_cell(cell_no), new_node);
 | 
						|
 | 
						|
	UT_LIST_ADD_LAST(all_list, mem_all_list_base, new_node);
 | 
						|
 | 
						|
	mem_n_created_heaps++;
 | 
						|
 | 
						|
	mutex_exit(&mem_hash_mutex);
 | 
						|
}
 | 
						|
 | 
						|
/***************************************************************//**
 | 
						|
Removes a memory heap (which is going to be freed by the caller)
 | 
						|
from the list of live memory heaps. Returns the size of the heap
 | 
						|
in terms of how much memory in bytes was allocated for the user of
 | 
						|
the heap (not the total space occupied by the heap).
 | 
						|
Also validates the heap.
 | 
						|
NOTE: This function does not free the storage occupied by the
 | 
						|
heap itself, only the node in the list of heaps. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_hash_remove(
 | 
						|
/*============*/
 | 
						|
	mem_heap_t*	heap,	   /*!< in: the heap to be freed */
 | 
						|
	const char*	file_name, /*!< in: file name of freeing */
 | 
						|
	ulint		line)	   /*!< in: line where freed */
 | 
						|
{
 | 
						|
	mem_hash_node_t*	node;
 | 
						|
	ulint			cell_no;
 | 
						|
	ibool			error;
 | 
						|
	ulint			size;
 | 
						|
 | 
						|
	ut_ad(mem_heap_check(heap));
 | 
						|
 | 
						|
	mutex_enter(&mem_hash_mutex);
 | 
						|
 | 
						|
	cell_no = ut_hash_ulint((ulint)heap, MEM_HASH_SIZE);
 | 
						|
 | 
						|
	/* Look for the heap in the hash table list */
 | 
						|
	node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(cell_no));
 | 
						|
 | 
						|
	while (node != NULL) {
 | 
						|
		if (node->heap == heap) {
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		node = UT_LIST_GET_NEXT(list, node);
 | 
						|
	}
 | 
						|
 | 
						|
	if (node == NULL) {
 | 
						|
		fprintf(stderr,
 | 
						|
			"Memory heap or buffer freed in %s line %lu"
 | 
						|
			" did not exist.\n",
 | 
						|
			file_name, (ulong) line);
 | 
						|
		ut_error;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Remove from lists */
 | 
						|
	UT_LIST_REMOVE(list, *mem_hash_get_nth_cell(cell_no), node);
 | 
						|
 | 
						|
	UT_LIST_REMOVE(all_list, mem_all_list_base, node);
 | 
						|
 | 
						|
	/* Validate the heap which will be freed */
 | 
						|
	mem_heap_validate_or_print(node->heap, NULL, FALSE, &error, &size,
 | 
						|
				   NULL, NULL);
 | 
						|
	if (error) {
 | 
						|
		fprintf(stderr,
 | 
						|
			"Inconsistency in memory heap or"
 | 
						|
			" buffer n:o %lu created\n"
 | 
						|
			"in %s line %lu and tried to free in %s line %lu.\n"
 | 
						|
			"Hex dump of 400 bytes around memory heap"
 | 
						|
			" first block start:\n",
 | 
						|
			node->nth_heap, node->file_name, (ulong) node->line,
 | 
						|
			file_name, (ulong) line);
 | 
						|
		ut_print_buf(stderr, (byte*)node->heap - 200, 400);
 | 
						|
		fputs("\nDump of the mem heap:\n", stderr);
 | 
						|
		mem_heap_validate_or_print(node->heap, NULL, TRUE, &error,
 | 
						|
					   &size, NULL, NULL);
 | 
						|
		ut_error;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Free the memory occupied by the node struct */
 | 
						|
	ut_free(node);
 | 
						|
 | 
						|
	mem_current_allocated_memory -= size;
 | 
						|
 | 
						|
	mutex_exit(&mem_hash_mutex);
 | 
						|
}
 | 
						|
#endif /* UNIV_MEM_DEBUG */
 | 
						|
 | 
						|
#if defined UNIV_MEM_DEBUG || defined UNIV_DEBUG
 | 
						|
/***************************************************************//**
 | 
						|
Checks a memory heap for consistency and prints the contents if requested.
 | 
						|
Outputs the sum of sizes of buffers given to the user (only in
 | 
						|
the debug version), the physical size of the heap and the number of
 | 
						|
blocks in the heap. In case of error returns 0 as sizes and number
 | 
						|
of blocks. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_heap_validate_or_print(
 | 
						|
/*=======================*/
 | 
						|
	mem_heap_t*	heap,	/*!< in: memory heap */
 | 
						|
	byte*		top __attribute__((unused)),
 | 
						|
				/*!< in: calculate and validate only until
 | 
						|
				this top pointer in the heap is reached,
 | 
						|
				if this pointer is NULL, ignored */
 | 
						|
	ibool		print,	/*!< in: if TRUE, prints the contents
 | 
						|
				of the heap; works only in
 | 
						|
				the debug version */
 | 
						|
	ibool*		error,	/*!< out: TRUE if error */
 | 
						|
	ulint*		us_size,/*!< out: allocated memory
 | 
						|
				(for the user) in the heap,
 | 
						|
				if a NULL pointer is passed as this
 | 
						|
				argument, it is ignored; in the
 | 
						|
				non-debug version this is always -1 */
 | 
						|
	ulint*		ph_size,/*!< out: physical size of the heap,
 | 
						|
				if a NULL pointer is passed as this
 | 
						|
				argument, it is ignored */
 | 
						|
	ulint*		n_blocks) /*!< out: number of blocks in the heap,
 | 
						|
				if a NULL pointer is passed as this
 | 
						|
				argument, it is ignored */
 | 
						|
{
 | 
						|
	mem_block_t*	block;
 | 
						|
	ulint		total_len	= 0;
 | 
						|
	ulint		block_count	= 0;
 | 
						|
	ulint		phys_len	= 0;
 | 
						|
#ifdef UNIV_MEM_DEBUG
 | 
						|
	ulint		len;
 | 
						|
	byte*		field;
 | 
						|
	byte*		user_field;
 | 
						|
	ulint		check_field;
 | 
						|
#endif
 | 
						|
 | 
						|
	/* Pessimistically, we set the parameters to error values */
 | 
						|
	if (us_size != NULL) {
 | 
						|
		*us_size = 0;
 | 
						|
	}
 | 
						|
	if (ph_size != NULL) {
 | 
						|
		*ph_size = 0;
 | 
						|
	}
 | 
						|
	if (n_blocks != NULL) {
 | 
						|
		*n_blocks = 0;
 | 
						|
	}
 | 
						|
	*error = TRUE;
 | 
						|
 | 
						|
	block = heap;
 | 
						|
 | 
						|
	if (block->magic_n != MEM_BLOCK_MAGIC_N) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (print) {
 | 
						|
		fputs("Memory heap:", stderr);
 | 
						|
	}
 | 
						|
 | 
						|
	while (block != NULL) {
 | 
						|
		phys_len += mem_block_get_len(block);
 | 
						|
 | 
						|
		if ((block->type == MEM_HEAP_BUFFER)
 | 
						|
		    && (mem_block_get_len(block) > UNIV_PAGE_SIZE)) {
 | 
						|
 | 
						|
			fprintf(stderr,
 | 
						|
				"InnoDB: Error: mem block %p"
 | 
						|
				" length %lu > UNIV_PAGE_SIZE\n",
 | 
						|
				(void*) block,
 | 
						|
				(ulong) mem_block_get_len(block));
 | 
						|
			/* error */
 | 
						|
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
#ifdef UNIV_MEM_DEBUG
 | 
						|
		/* We can trace the fields of the block only in the debug
 | 
						|
		version */
 | 
						|
		if (print) {
 | 
						|
			fprintf(stderr, " Block %ld:", block_count);
 | 
						|
		}
 | 
						|
 | 
						|
		field = (byte*)block + mem_block_get_start(block);
 | 
						|
 | 
						|
		if (top && (field == top)) {
 | 
						|
 | 
						|
			goto completed;
 | 
						|
		}
 | 
						|
 | 
						|
		while (field < (byte*)block + mem_block_get_free(block)) {
 | 
						|
 | 
						|
			/* Calculate the pointer to the storage
 | 
						|
			which was given to the user */
 | 
						|
 | 
						|
			user_field = field + MEM_FIELD_HEADER_SIZE;
 | 
						|
 | 
						|
			len = mem_field_header_get_len(user_field);
 | 
						|
 | 
						|
			if (print) {
 | 
						|
				ut_print_buf(stderr, user_field, len);
 | 
						|
				putc('\n', stderr);
 | 
						|
			}
 | 
						|
 | 
						|
			total_len += len;
 | 
						|
			check_field = mem_field_header_get_check(user_field);
 | 
						|
 | 
						|
			if (check_field
 | 
						|
			    != mem_field_trailer_get_check(user_field)) {
 | 
						|
				/* error */
 | 
						|
 | 
						|
				fprintf(stderr,
 | 
						|
					"InnoDB: Error: block %lx mem"
 | 
						|
					" field %lx len %lu\n"
 | 
						|
					"InnoDB: header check field is"
 | 
						|
					" %lx but trailer %lx\n",
 | 
						|
					(ulint)block,
 | 
						|
					(ulint)field, len, check_field,
 | 
						|
					mem_field_trailer_get_check(
 | 
						|
						user_field));
 | 
						|
 | 
						|
				return;
 | 
						|
			}
 | 
						|
 | 
						|
			/* Move to next field */
 | 
						|
			field = field + MEM_SPACE_NEEDED(len);
 | 
						|
 | 
						|
			if (top && (field == top)) {
 | 
						|
 | 
						|
				goto completed;
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		/* At the end check that we have arrived to the first free
 | 
						|
		position */
 | 
						|
 | 
						|
		if (field != (byte*)block + mem_block_get_free(block)) {
 | 
						|
			/* error */
 | 
						|
 | 
						|
			fprintf(stderr,
 | 
						|
				"InnoDB: Error: block %lx end of"
 | 
						|
				" mem fields %lx\n"
 | 
						|
				"InnoDB: but block free at %lx\n",
 | 
						|
				(ulint)block, (ulint)field,
 | 
						|
				(ulint)((byte*)block
 | 
						|
					+ mem_block_get_free(block)));
 | 
						|
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
		block = UT_LIST_GET_NEXT(list, block);
 | 
						|
		block_count++;
 | 
						|
	}
 | 
						|
#ifdef UNIV_MEM_DEBUG
 | 
						|
completed:
 | 
						|
#endif
 | 
						|
	if (us_size != NULL) {
 | 
						|
		*us_size = total_len;
 | 
						|
	}
 | 
						|
	if (ph_size != NULL) {
 | 
						|
		*ph_size = phys_len;
 | 
						|
	}
 | 
						|
	if (n_blocks != NULL) {
 | 
						|
		*n_blocks = block_count;
 | 
						|
	}
 | 
						|
	*error = FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**************************************************************//**
 | 
						|
Prints the contents of a memory heap. */
 | 
						|
static
 | 
						|
void
 | 
						|
mem_heap_print(
 | 
						|
/*===========*/
 | 
						|
	mem_heap_t*	heap)	/*!< in: memory heap */
 | 
						|
{
 | 
						|
	ibool	error;
 | 
						|
	ulint	us_size;
 | 
						|
	ulint	phys_size;
 | 
						|
	ulint	n_blocks;
 | 
						|
 | 
						|
	ut_ad(mem_heap_check(heap));
 | 
						|
 | 
						|
	mem_heap_validate_or_print(heap, NULL, TRUE, &error,
 | 
						|
				   &us_size, &phys_size, &n_blocks);
 | 
						|
	fprintf(stderr,
 | 
						|
		"\nheap type: %lu; size: user size %lu;"
 | 
						|
		" physical size %lu; blocks %lu.\n",
 | 
						|
		(ulong) heap->type, (ulong) us_size,
 | 
						|
		(ulong) phys_size, (ulong) n_blocks);
 | 
						|
	ut_a(!error);
 | 
						|
}
 | 
						|
 | 
						|
/**************************************************************//**
 | 
						|
Validates the contents of a memory heap.
 | 
						|
@return	TRUE if ok */
 | 
						|
UNIV_INTERN
 | 
						|
ibool
 | 
						|
mem_heap_validate(
 | 
						|
/*==============*/
 | 
						|
	mem_heap_t*	heap)	/*!< in: memory heap */
 | 
						|
{
 | 
						|
	ibool	error;
 | 
						|
	ulint	us_size;
 | 
						|
	ulint	phys_size;
 | 
						|
	ulint	n_blocks;
 | 
						|
 | 
						|
	ut_ad(mem_heap_check(heap));
 | 
						|
 | 
						|
	mem_heap_validate_or_print(heap, NULL, FALSE, &error, &us_size,
 | 
						|
				   &phys_size, &n_blocks);
 | 
						|
	if (error) {
 | 
						|
		mem_heap_print(heap);
 | 
						|
	}
 | 
						|
 | 
						|
	ut_a(!error);
 | 
						|
 | 
						|
	return(TRUE);
 | 
						|
}
 | 
						|
#endif /* UNIV_MEM_DEBUG || UNIV_DEBUG */
 | 
						|
 | 
						|
#ifdef UNIV_DEBUG
 | 
						|
/**************************************************************//**
 | 
						|
Checks that an object is a memory heap (or a block of it).
 | 
						|
@return	TRUE if ok */
 | 
						|
UNIV_INTERN
 | 
						|
ibool
 | 
						|
mem_heap_check(
 | 
						|
/*===========*/
 | 
						|
	mem_heap_t*	heap)	/*!< in: memory heap */
 | 
						|
{
 | 
						|
	ut_a(heap->magic_n == MEM_BLOCK_MAGIC_N);
 | 
						|
 | 
						|
	return(TRUE);
 | 
						|
}
 | 
						|
#endif /* UNIV_DEBUG */
 | 
						|
 | 
						|
#ifdef UNIV_MEM_DEBUG
 | 
						|
/*****************************************************************//**
 | 
						|
TRUE if no memory is currently allocated.
 | 
						|
@return	TRUE if no heaps exist */
 | 
						|
UNIV_INTERN
 | 
						|
ibool
 | 
						|
mem_all_freed(void)
 | 
						|
/*===============*/
 | 
						|
{
 | 
						|
	mem_hash_node_t*	node;
 | 
						|
	ulint			heap_count	= 0;
 | 
						|
	ulint			i;
 | 
						|
 | 
						|
	mem_validate();
 | 
						|
 | 
						|
	mutex_enter(&mem_hash_mutex);
 | 
						|
 | 
						|
	for (i = 0; i < MEM_HASH_SIZE; i++) {
 | 
						|
 | 
						|
		node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(i));
 | 
						|
		while (node != NULL) {
 | 
						|
			heap_count++;
 | 
						|
			node = UT_LIST_GET_NEXT(list, node);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_exit(&mem_hash_mutex);
 | 
						|
 | 
						|
	if (heap_count == 0) {
 | 
						|
# ifndef UNIV_HOTBACKUP
 | 
						|
		ut_a(mem_pool_get_reserved(mem_comm_pool) == 0);
 | 
						|
# endif /* !UNIV_HOTBACKUP */
 | 
						|
 | 
						|
		return(TRUE);
 | 
						|
	} else {
 | 
						|
		return(FALSE);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************//**
 | 
						|
Validates the dynamic memory allocation system.
 | 
						|
@return	TRUE if error */
 | 
						|
UNIV_INTERN
 | 
						|
ibool
 | 
						|
mem_validate_no_assert(void)
 | 
						|
/*========================*/
 | 
						|
{
 | 
						|
	mem_hash_node_t*	node;
 | 
						|
	ulint			n_heaps			= 0;
 | 
						|
	ulint			allocated_mem;
 | 
						|
	ulint			ph_size;
 | 
						|
	ulint			total_allocated_mem	= 0;
 | 
						|
	ibool			error			= FALSE;
 | 
						|
	ulint			n_blocks;
 | 
						|
	ulint			i;
 | 
						|
 | 
						|
# ifndef UNIV_HOTBACKUP
 | 
						|
	mem_pool_validate(mem_comm_pool);
 | 
						|
# endif /* !UNIV_HOTBACKUP */
 | 
						|
 | 
						|
	mutex_enter(&mem_hash_mutex);
 | 
						|
 | 
						|
	for (i = 0; i < MEM_HASH_SIZE; i++) {
 | 
						|
 | 
						|
		node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(i));
 | 
						|
 | 
						|
		while (node != NULL) {
 | 
						|
			n_heaps++;
 | 
						|
 | 
						|
			mem_heap_validate_or_print(node->heap, NULL,
 | 
						|
						   FALSE, &error,
 | 
						|
						   &allocated_mem,
 | 
						|
						   &ph_size, &n_blocks);
 | 
						|
 | 
						|
			if (error) {
 | 
						|
				fprintf(stderr,
 | 
						|
					"\nERROR!!!!!!!!!!!!!!!!!!!"
 | 
						|
					"!!!!!!!!!!!!!!!!!!!!!!!\n\n"
 | 
						|
					"Inconsistency in memory heap"
 | 
						|
					" or buffer created\n"
 | 
						|
					"in %s line %lu.\n",
 | 
						|
					node->file_name, node->line);
 | 
						|
 | 
						|
				mutex_exit(&mem_hash_mutex);
 | 
						|
 | 
						|
				return(TRUE);
 | 
						|
			}
 | 
						|
 | 
						|
			total_allocated_mem += allocated_mem;
 | 
						|
			node = UT_LIST_GET_NEXT(list, node);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if ((n_heaps == 0) && (mem_current_allocated_memory != 0)) {
 | 
						|
		error = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (mem_total_allocated_memory < mem_current_allocated_memory) {
 | 
						|
		error = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (mem_max_allocated_memory > mem_total_allocated_memory) {
 | 
						|
		error = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (mem_n_created_heaps < n_heaps) {
 | 
						|
		error = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	mutex_exit(&mem_hash_mutex);
 | 
						|
 | 
						|
	return(error);
 | 
						|
}
 | 
						|
 | 
						|
/************************************************************//**
 | 
						|
Validates the dynamic memory
 | 
						|
@return	TRUE if ok */
 | 
						|
UNIV_INTERN
 | 
						|
ibool
 | 
						|
mem_validate(void)
 | 
						|
/*==============*/
 | 
						|
{
 | 
						|
	ut_a(!mem_validate_no_assert());
 | 
						|
 | 
						|
	return(TRUE);
 | 
						|
}
 | 
						|
#endif /* UNIV_MEM_DEBUG */
 | 
						|
 | 
						|
/************************************************************//**
 | 
						|
Tries to find neigboring memory allocation blocks and dumps to stderr
 | 
						|
the neighborhood of a given pointer. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_analyze_corruption(
 | 
						|
/*===================*/
 | 
						|
	void*	ptr)	/*!< in: pointer to place of possible corruption */
 | 
						|
{
 | 
						|
	byte*	p;
 | 
						|
	ulint	i;
 | 
						|
	ulint	dist;
 | 
						|
 | 
						|
	fputs("InnoDB: Apparent memory corruption: mem dump ", stderr);
 | 
						|
	ut_print_buf(stderr, (byte*)ptr - 250, 500);
 | 
						|
 | 
						|
	fputs("\nInnoDB: Scanning backward trying to find"
 | 
						|
	      " previous allocated mem blocks\n", stderr);
 | 
						|
 | 
						|
	p = (byte*)ptr;
 | 
						|
	dist = 0;
 | 
						|
 | 
						|
	for (i = 0; i < 10; i++) {
 | 
						|
		for (;;) {
 | 
						|
			if (((ulint)p) % 4 == 0) {
 | 
						|
 | 
						|
				if (*((ulint*)p) == MEM_BLOCK_MAGIC_N) {
 | 
						|
					fprintf(stderr,
 | 
						|
						"Mem block at - %lu,"
 | 
						|
						" file %s, line %lu\n",
 | 
						|
						(ulong) dist,
 | 
						|
						(p + sizeof(ulint)),
 | 
						|
						(ulong)
 | 
						|
						(*(ulint*)(p + 8
 | 
						|
							   + sizeof(ulint))));
 | 
						|
 | 
						|
					break;
 | 
						|
				}
 | 
						|
 | 
						|
				if (*((ulint*)p) == MEM_FREED_BLOCK_MAGIC_N) {
 | 
						|
					fprintf(stderr,
 | 
						|
						"Freed mem block at - %lu,"
 | 
						|
						" file %s, line %lu\n",
 | 
						|
						(ulong) dist,
 | 
						|
						(p + sizeof(ulint)),
 | 
						|
						(ulong)
 | 
						|
						(*(ulint*)(p + 8
 | 
						|
							   + sizeof(ulint))));
 | 
						|
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			p--;
 | 
						|
			dist++;
 | 
						|
		}
 | 
						|
 | 
						|
		p--;
 | 
						|
		dist++;
 | 
						|
	}
 | 
						|
 | 
						|
	fprintf(stderr,
 | 
						|
		"InnoDB: Scanning forward trying to find next"
 | 
						|
		" allocated mem blocks\n");
 | 
						|
 | 
						|
	p = (byte*)ptr;
 | 
						|
	dist = 0;
 | 
						|
 | 
						|
	for (i = 0; i < 10; i++) {
 | 
						|
		for (;;) {
 | 
						|
			if (((ulint)p) % 4 == 0) {
 | 
						|
 | 
						|
				if (*((ulint*)p) == MEM_BLOCK_MAGIC_N) {
 | 
						|
					fprintf(stderr,
 | 
						|
						"Mem block at + %lu, file %s,"
 | 
						|
						" line %lu\n",
 | 
						|
						(ulong) dist,
 | 
						|
						(p + sizeof(ulint)),
 | 
						|
						(ulong)
 | 
						|
						(*(ulint*)(p + 8
 | 
						|
							   + sizeof(ulint))));
 | 
						|
 | 
						|
					break;
 | 
						|
				}
 | 
						|
 | 
						|
				if (*((ulint*)p) == MEM_FREED_BLOCK_MAGIC_N) {
 | 
						|
					fprintf(stderr,
 | 
						|
						"Freed mem block at + %lu,"
 | 
						|
						" file %s, line %lu\n",
 | 
						|
						(ulong) dist,
 | 
						|
						(p + sizeof(ulint)),
 | 
						|
						(ulong)
 | 
						|
						(*(ulint*)(p + 8
 | 
						|
							   + sizeof(ulint))));
 | 
						|
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			p++;
 | 
						|
			dist++;
 | 
						|
		}
 | 
						|
 | 
						|
		p++;
 | 
						|
		dist++;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
#ifndef UNIV_HOTBACKUP
 | 
						|
/*****************************************************************//**
 | 
						|
Prints information of dynamic memory usage and currently allocated
 | 
						|
memory heaps or buffers. Can only be used in the debug version. */
 | 
						|
static
 | 
						|
void
 | 
						|
mem_print_info_low(
 | 
						|
/*===============*/
 | 
						|
	ibool	print_all)	/*!< in: if TRUE, all heaps are printed,
 | 
						|
				else only the heaps allocated after the
 | 
						|
				previous call of this function */
 | 
						|
{
 | 
						|
#ifdef UNIV_MEM_DEBUG
 | 
						|
	mem_hash_node_t*	node;
 | 
						|
	ulint			n_heaps			= 0;
 | 
						|
	ulint			allocated_mem;
 | 
						|
	ulint			ph_size;
 | 
						|
	ulint			total_allocated_mem	= 0;
 | 
						|
	ibool			error;
 | 
						|
	ulint			n_blocks;
 | 
						|
#endif
 | 
						|
	FILE*			outfile;
 | 
						|
 | 
						|
	/* outfile = fopen("ibdebug", "a"); */
 | 
						|
 | 
						|
	outfile = stdout;
 | 
						|
 | 
						|
	fprintf(outfile, "\n");
 | 
						|
	fprintf(outfile,
 | 
						|
		"________________________________________________________\n");
 | 
						|
	fprintf(outfile, "MEMORY ALLOCATION INFORMATION\n\n");
 | 
						|
 | 
						|
#ifndef UNIV_MEM_DEBUG
 | 
						|
 | 
						|
	UT_NOT_USED(print_all);
 | 
						|
 | 
						|
	mem_pool_print_info(outfile, mem_comm_pool);
 | 
						|
 | 
						|
	fprintf(outfile,
 | 
						|
		"Sorry, non-debug version cannot give more memory info\n");
 | 
						|
 | 
						|
	/* fclose(outfile); */
 | 
						|
 | 
						|
	return;
 | 
						|
#else
 | 
						|
	mutex_enter(&mem_hash_mutex);
 | 
						|
 | 
						|
	fprintf(outfile, "LIST OF CREATED HEAPS AND ALLOCATED BUFFERS: \n\n");
 | 
						|
 | 
						|
	if (!print_all) {
 | 
						|
		fprintf(outfile, "AFTER THE LAST PRINT INFO\n");
 | 
						|
	}
 | 
						|
 | 
						|
	node = UT_LIST_GET_FIRST(mem_all_list_base);
 | 
						|
 | 
						|
	while (node != NULL) {
 | 
						|
		n_heaps++;
 | 
						|
 | 
						|
		if (!print_all && node->nth_heap < mem_last_print_info) {
 | 
						|
 | 
						|
			goto next_heap;
 | 
						|
		}
 | 
						|
 | 
						|
		mem_heap_validate_or_print(node->heap, NULL,
 | 
						|
					   FALSE, &error, &allocated_mem,
 | 
						|
					   &ph_size, &n_blocks);
 | 
						|
		total_allocated_mem += allocated_mem;
 | 
						|
 | 
						|
		fprintf(outfile,
 | 
						|
			"%lu: file %s line %lu of size %lu phys.size %lu"
 | 
						|
			" with %lu blocks, type %lu\n",
 | 
						|
			node->nth_heap, node->file_name, node->line,
 | 
						|
			allocated_mem, ph_size, n_blocks,
 | 
						|
			(node->heap)->type);
 | 
						|
next_heap:
 | 
						|
		node = UT_LIST_GET_NEXT(all_list, node);
 | 
						|
	}
 | 
						|
 | 
						|
	fprintf(outfile, "\n");
 | 
						|
 | 
						|
	fprintf(outfile, "Current allocated memory              : %lu\n",
 | 
						|
		mem_current_allocated_memory);
 | 
						|
	fprintf(outfile, "Current allocated heaps and buffers   : %lu\n",
 | 
						|
		n_heaps);
 | 
						|
	fprintf(outfile, "Cumulative allocated memory           : %lu\n",
 | 
						|
		mem_total_allocated_memory);
 | 
						|
	fprintf(outfile, "Maximum allocated memory              : %lu\n",
 | 
						|
		mem_max_allocated_memory);
 | 
						|
	fprintf(outfile, "Cumulative created heaps and buffers  : %lu\n",
 | 
						|
		mem_n_created_heaps);
 | 
						|
	fprintf(outfile, "Cumulative number of allocations      : %lu\n",
 | 
						|
		mem_n_allocations);
 | 
						|
 | 
						|
	mem_last_print_info = mem_n_created_heaps;
 | 
						|
 | 
						|
	mutex_exit(&mem_hash_mutex);
 | 
						|
 | 
						|
	mem_pool_print_info(outfile, mem_comm_pool);
 | 
						|
 | 
						|
	/*	mem_validate(); */
 | 
						|
 | 
						|
	/*	fclose(outfile); */
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************//**
 | 
						|
Prints information of dynamic memory usage and currently allocated memory
 | 
						|
heaps or buffers. Can only be used in the debug version. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_print_info(void)
 | 
						|
/*================*/
 | 
						|
{
 | 
						|
	mem_print_info_low(TRUE);
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************//**
 | 
						|
Prints information of dynamic memory usage and currently allocated memory
 | 
						|
heaps or buffers since the last ..._print_info or..._print_new_info. */
 | 
						|
UNIV_INTERN
 | 
						|
void
 | 
						|
mem_print_new_info(void)
 | 
						|
/*====================*/
 | 
						|
{
 | 
						|
	mem_print_info_low(FALSE);
 | 
						|
}
 | 
						|
#endif /* !UNIV_HOTBACKUP */
 |