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