0N/A/*
2362N/A * Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#if defined(DEBUG)
0N/A
0N/A#include "debug_util.h"
0N/A
0N/A#define DMEM_MIN(a,b) (a) < (b) ? (a) : (b)
0N/A#define DMEM_MAX(a,b) (a) > (b) ? (a) : (b)
0N/A
0N/Atypedef char byte_t;
0N/A
0N/Astatic const byte_t ByteInited = '\xCD';
0N/Astatic const byte_t ByteFreed = '\xDD';
0N/Astatic const byte_t ByteGuard = '\xFD';
0N/A
0N/Aenum {
0N/A MAX_LINENUM = 50000, /* I certainly hope we don't have source files bigger than this */
0N/A MAX_CHECK_BYTES = 27, /* max bytes to check at start of block */
0N/A MAX_GUARD_BYTES = 8, /* size of guard areas on either side of a block */
0N/A MAX_DECIMAL_DIGITS = 15
0N/A};
0N/A
0N/A/* Debug Info Header to precede allocated block */
0N/Atypedef struct MemoryBlockHeader {
0N/A char filename[FILENAME_MAX+1]; /* filename where alloc occurred */
0N/A int linenumber; /* line where alloc occurred */
0N/A size_t size; /* size of the allocation */
0N/A int order; /* the order the block was allocated in */
0N/A struct MemoryListLink * listEnter; /* pointer to the free list node */
0N/A byte_t guard[MAX_GUARD_BYTES]; /* guard area for underrun check */
0N/A} MemoryBlockHeader;
0N/A
0N/A/* Tail to follow allocated block */
0N/Atypedef struct MemoryBlockTail {
0N/A byte_t guard[MAX_GUARD_BYTES]; /* guard area overrun check */
0N/A} MemoryBlockTail;
0N/A
0N/A/* Linked list of allocated memory blocks */
0N/Atypedef struct MemoryListLink {
0N/A struct MemoryListLink * next;
0N/A MemoryBlockHeader * header;
0N/A int freed;
0N/A} MemoryListLink;
0N/A
0N/A/**************************************************
0N/A * Global Data structures
0N/A */
0N/Astatic DMemState DMemGlobalState;
0N/Aextern const DMemState * DMemStatePtr = &DMemGlobalState;
0N/Astatic MemoryListLink MemoryList = {NULL,NULL,FALSE};
0N/Astatic dmutex_t DMemMutex = NULL;
0N/A
0N/A/**************************************************/
0N/A
0N/A/*************************************************
0N/A * Client callback invocation functions
0N/A */
0N/Astatic void * DMem_ClientAllocate(size_t size) {
0N/A if (DMemGlobalState.pfnAlloc != NULL) {
0N/A return (*DMemGlobalState.pfnAlloc)(size);
0N/A }
0N/A return malloc(size);
0N/A}
0N/A
0N/Astatic void DMem_ClientFree(void * ptr) {
0N/A if (DMemGlobalState.pfnFree != NULL) {
0N/A (*DMemGlobalState.pfnFree)(ptr);
0N/A }
0N/A free(ptr);
0N/A}
0N/A
0N/Astatic dbool_t DMem_ClientCheckPtr(void * ptr, size_t size) {
0N/A if (DMemGlobalState.pfnCheckPtr != NULL) {
0N/A return (*DMemGlobalState.pfnCheckPtr)(ptr, size);
0N/A }
0N/A return ptr != NULL;
0N/A}
0N/A
0N/A/**************************************************/
0N/A
0N/A/*************************************************
0N/A * Debug Memory Manager implementation
0N/A */
0N/A
0N/Astatic MemoryListLink * DMem_TrackBlock(MemoryBlockHeader * header) {
0N/A MemoryListLink * link;
0N/A
0N/A link = (MemoryListLink *)DMem_ClientAllocate(sizeof(MemoryListLink));
0N/A if (link != NULL) {
0N/A link->header = header;
0N/A link->header->listEnter = link;
0N/A link->next = MemoryList.next;
0N/A link->freed = FALSE;
0N/A MemoryList.next = link;
0N/A }
0N/A
0N/A return link;
0N/A}
0N/A
0N/Astatic int DMem_VerifyGuardArea(const byte_t * area) {
0N/A int nbyte;
0N/A
0N/A for ( nbyte = 0; nbyte < MAX_GUARD_BYTES; nbyte++ ) {
0N/A if (area[nbyte] != ByteGuard) {
0N/A return FALSE;
0N/A }
0N/A }
0N/A return TRUE;
0N/A}
0N/A
0N/Astatic void DMem_VerifyHeader(MemoryBlockHeader * header) {
0N/A DASSERTMSG( DMem_ClientCheckPtr(header, sizeof(MemoryBlockHeader)), "Invalid header" );
0N/A DASSERTMSG( DMem_VerifyGuardArea(header->guard), "Header corruption, possible underwrite" );
0N/A DASSERTMSG( header->linenumber > 0 && header->linenumber < MAX_LINENUM, "Header corruption, bad line number" );
0N/A DASSERTMSG( header->size <= DMemGlobalState.biggestBlock, "Header corruption, block size is too large");
0N/A DASSERTMSG( header->order <= DMemGlobalState.totalAllocs, "Header corruption, block order out of range");
0N/A}
0N/A
0N/Astatic void DMem_VerifyTail(MemoryBlockTail * tail) {
0N/A DASSERTMSG( DMem_ClientCheckPtr(tail, sizeof(MemoryBlockTail)), "Tail corruption, invalid pointer");
0N/A DASSERTMSG( DMem_VerifyGuardArea(tail->guard), "Tail corruption, possible overwrite" );
0N/A}
0N/A
0N/Astatic MemoryBlockHeader * DMem_VerifyBlock(void * memptr) {
0N/A MemoryBlockHeader * header;
0N/A MemoryBlockTail * tail;
0N/A
0N/A /* check if the pointer is valid */
0N/A DASSERTMSG( DMem_ClientCheckPtr(memptr, 1), "Invalid pointer");
0N/A
0N/A /* check if the block header is valid */
0N/A header = (MemoryBlockHeader *)((byte_t *)memptr - sizeof(MemoryBlockHeader));
0N/A DMem_VerifyHeader(header);
0N/A /* check that the memory itself is valid */
0N/A DASSERTMSG( DMem_ClientCheckPtr(memptr, DMEM_MIN(MAX_CHECK_BYTES,header->size)), "Block memory invalid" );
0N/A /* check that the pointer to the alloc list is valid */
0N/A DASSERTMSG( DMem_ClientCheckPtr(header->listEnter, sizeof(MemoryListLink)), "Header corruption, alloc list pointer invalid" );
0N/A /* check the tail of the block for overruns */
0N/A tail = (MemoryBlockTail *) ( (byte_t *)memptr + header->size );
0N/A DMem_VerifyTail(tail);
0N/A
0N/A return header;
0N/A}
0N/A
0N/Astatic MemoryBlockHeader * DMem_GetHeader(void * memptr) {
0N/A MemoryBlockHeader * header = DMem_VerifyBlock(memptr);
0N/A return header;
0N/A}
0N/A
0N/A/*
0N/A * Should be called before any other DMem_XXX function
0N/A */
0N/Avoid DMem_Initialize() {
0N/A DMemMutex = DMutex_Create();
0N/A DMutex_Enter(DMemMutex);
0N/A DMemGlobalState.pfnAlloc = NULL;
0N/A DMemGlobalState.pfnFree = NULL;
0N/A DMemGlobalState.pfnCheckPtr = NULL;
0N/A DMemGlobalState.biggestBlock = 0;
0N/A DMemGlobalState.maxHeap = INT_MAX;
0N/A DMemGlobalState.totalHeapUsed = 0;
0N/A DMemGlobalState.failNextAlloc = FALSE;
0N/A DMemGlobalState.totalAllocs = 0;
0N/A DMutex_Exit(DMemMutex);
0N/A}
0N/A
0N/Avoid DMem_Shutdown() {
0N/A DMutex_Destroy(DMemMutex);
0N/A}
0N/A/*
0N/A * Allocates a block of memory, reserving extra space at the start and end of the
0N/A * block to store debug info on where the block was allocated, it's size, and
0N/A * 'guard' areas to catch overwrite/underwrite bugs
0N/A */
0N/Avoid * DMem_AllocateBlock(size_t size, const char * filename, int linenumber) {
0N/A MemoryBlockHeader * header;
0N/A MemoryBlockTail * tail;
0N/A size_t debugBlockSize;
0N/A byte_t * memptr = NULL;
0N/A
0N/A DMutex_Enter(DMemMutex);
0N/A if (DMemGlobalState.failNextAlloc) {
0N/A /* force an allocation failure if so ordered */
0N/A DMemGlobalState.failNextAlloc = FALSE; /* reset flag */
0N/A goto Exit;
0N/A }
0N/A
0N/A /* allocate a block large enough to hold extra debug info */
0N/A debugBlockSize = sizeof(MemoryBlockHeader) + size + sizeof(MemoryBlockTail);
0N/A header = (MemoryBlockHeader *)DMem_ClientAllocate(debugBlockSize);
0N/A if (header == NULL) {
0N/A goto Exit;
0N/A }
0N/A
0N/A /* add block to list of allocated memory */
0N/A header->listEnter = DMem_TrackBlock(header);
0N/A if ( header->listEnter == NULL ) {
0N/A goto Exit;
0N/A }
0N/A
0N/A /* store size of requested block */
0N/A header->size = size;
0N/A /* update maximum block size */
0N/A DMemGlobalState.biggestBlock = DMEM_MAX(header->size, DMemGlobalState.biggestBlock);
0N/A /* update used memory total */
0N/A DMemGlobalState.totalHeapUsed += header->size;
0N/A /* store filename and linenumber where allocation routine was called */
0N/A strncpy(header->filename, filename, FILENAME_MAX);
0N/A header->linenumber = linenumber;
0N/A /* store the order the block was allocated in */
0N/A header->order = DMemGlobalState.totalAllocs++;
0N/A /* initialize memory to a recognizable 'inited' value */
0N/A memptr = (byte_t *)header + sizeof(MemoryBlockHeader);
0N/A memset(memptr, ByteInited, size);
0N/A /* put guard area before block */
0N/A memset(header->guard, ByteGuard, MAX_GUARD_BYTES);
0N/A /* put guard area after block */
0N/A tail = (MemoryBlockTail *)(memptr + size);
0N/A memset(tail->guard, ByteGuard, MAX_GUARD_BYTES);
0N/A
0N/AExit:
0N/A DMutex_Exit(DMemMutex);
0N/A return memptr;
0N/A}
0N/A
0N/A/*
0N/A * Frees block of memory allocated with DMem_AllocateBlock
0N/A */
0N/Avoid DMem_FreeBlock(void * memptr) {
0N/A MemoryBlockHeader * header;
0N/A
0N/A DMutex_Enter(DMemMutex);
0N/A if ( memptr == NULL) {
0N/A goto Exit;
0N/A }
0N/A
0N/A /* get the debug block header preceding the allocated memory */
0N/A header = DMem_GetHeader(memptr);
0N/A /* fill memory with recognizable 'freed' value */
0N/A memset(memptr, ByteFreed, header->size);
0N/A /* mark block as freed */
0N/A header->listEnter->freed = TRUE;
0N/A /* update used memory total */
0N/A DMemGlobalState.totalHeapUsed -= header->size;
0N/AExit:
0N/A DMutex_Exit(DMemMutex);
0N/A}
0N/A
0N/Astatic void DMem_DumpHeader(MemoryBlockHeader * header) {
0N/A char report[FILENAME_MAX+MAX_DECIMAL_DIGITS*3+1];
0N/A static const char * reportFormat =
0N/A "file: %s, line %d\n"
0N/A "size: %d bytes\n"
0N/A "order: %d\n"
0N/A "-------";
0N/A
0N/A DMem_VerifyHeader(header);
0N/A sprintf(report, reportFormat, header->filename, header->linenumber, header->size, header->order);
0N/A DTRACE_PRINTLN(report);
0N/A}
0N/A
0N/A/*
0N/A * Call this function at shutdown time to report any leaked blocks
0N/A */
0N/Avoid DMem_ReportLeaks() {
0N/A MemoryListLink * link;
0N/A
0N/A DMutex_Enter(DMemMutex);
0N/A
0N/A /* Force memory leaks to be output regardless of trace settings */
0N/A DTrace_EnableFile(__FILE__, TRUE);
0N/A DTRACE_PRINTLN("--------------------------");
0N/A DTRACE_PRINTLN("Debug Memory Manager Leaks");
0N/A DTRACE_PRINTLN("--------------------------");
0N/A
0N/A /* walk through allocated list and dump any blocks not marked as freed */
0N/A link = MemoryList.next;
0N/A while (link != NULL) {
0N/A if ( !link->freed ) {
0N/A DMem_DumpHeader(link->header);
0N/A }
0N/A link = link->next;
0N/A }
0N/A
0N/A DMutex_Exit(DMemMutex);
0N/A}
0N/A
0N/Avoid DMem_SetAllocCallback( DMEM_ALLOCFN pfn ) {
0N/A DMutex_Enter(DMemMutex);
0N/A DMemGlobalState.pfnAlloc = pfn;
0N/A DMutex_Exit(DMemMutex);
0N/A}
0N/A
0N/Avoid DMem_SetFreeCallback( DMEM_FREEFN pfn ) {
0N/A DMutex_Enter(DMemMutex);
0N/A DMemGlobalState.pfnFree = pfn;
0N/A DMutex_Exit(DMemMutex);
0N/A}
0N/A
0N/Avoid DMem_SetCheckPtrCallback( DMEM_CHECKPTRFN pfn ) {
0N/A DMutex_Enter(DMemMutex);
0N/A DMemGlobalState.pfnCheckPtr = pfn;
0N/A DMutex_Exit(DMemMutex);
0N/A}
0N/A
0N/Avoid DMem_DisableMutex() {
0N/A DMemMutex = NULL;
0N/A}
0N/A
0N/A#endif /* defined(DEBUG) */
0N/A
0N/A/* The following line is only here to prevent compiler warnings
0N/A * on release (non-debug) builds
0N/A */
0N/Astatic int dummyVariable = 0;