1 /*
   2  * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #if defined(DEBUG)
  27 
  28 #include "debug_util.h"
  29 
  30 /* Use THIS_FILE when it is available. */
  31 #ifndef THIS_FILE
  32     #define THIS_FILE __FILE__
  33 #endif
  34 
  35 #define DMEM_MIN(a,b)   (a) < (b) ? (a) : (b)
  36 #define DMEM_MAX(a,b)   (a) > (b) ? (a) : (b)
  37 
  38 typedef char byte_t;
  39 
  40 static const byte_t ByteInited = '\xCD';
  41 static const byte_t ByteFreed = '\xDD';
  42 static const byte_t ByteGuard = '\xFD';
  43 
  44 enum {
  45     MAX_LINENUM = 50000,        /* I certainly hope we don't have source files bigger than this */
  46     MAX_CHECK_BYTES = 27,       /* max bytes to check at start of block */
  47     MAX_GUARD_BYTES = 8,        /* size of guard areas on either side of a block */
  48     MAX_DECIMAL_DIGITS = 15
  49 };
  50 
  51 /* Debug Info Header to precede allocated block */
  52 typedef struct MemoryBlockHeader {
  53     char                        filename[FILENAME_MAX+1]; /* filename where alloc occurred */
  54     int                         linenumber;             /* line where alloc occurred */
  55     size_t                      size;                   /* size of the allocation */
  56     int                         order;                  /* the order the block was allocated in */
  57     struct MemoryListLink *     listEnter;              /* pointer to the free list node */
  58     byte_t                      guard[MAX_GUARD_BYTES]; /* guard area for underrun check */
  59 } MemoryBlockHeader;
  60 
  61 /* Tail to follow allocated block */
  62 typedef struct MemoryBlockTail {
  63     byte_t                      guard[MAX_GUARD_BYTES]; /* guard area overrun check */
  64 } MemoryBlockTail;
  65 
  66 /* Linked list of allocated memory blocks */
  67 typedef struct MemoryListLink {
  68     struct MemoryListLink *     next;
  69     MemoryBlockHeader *         header;
  70     int                         freed;
  71 } MemoryListLink;
  72 
  73 /**************************************************
  74  * Global Data structures
  75  */
  76 static DMemState                DMemGlobalState;
  77 extern const DMemState *        DMemStatePtr;
  78 //Do this to avoid compiler warning about const extern initialization.
  79 const DMemState* DMemStatePtr = &DMemGlobalState;
  80 static MemoryListLink           MemoryList = {NULL,NULL,FALSE};
  81 static dmutex_t                 DMemMutex = NULL;
  82 
  83 /**************************************************/
  84 
  85 /*************************************************
  86  * Client callback invocation functions
  87  */
  88 static void * DMem_ClientAllocate(size_t size) {
  89     if (DMemGlobalState.pfnAlloc != NULL) {
  90         return (*DMemGlobalState.pfnAlloc)(size);
  91     }
  92     return malloc(size);
  93 }
  94 
  95 static void DMem_ClientFree(void * ptr) {
  96     if (DMemGlobalState.pfnFree != NULL) {
  97         (*DMemGlobalState.pfnFree)(ptr);
  98     }
  99     free(ptr);
 100 }
 101 
 102 static dbool_t DMem_ClientCheckPtr(void * ptr, size_t size) {
 103     if (DMemGlobalState.pfnCheckPtr != NULL) {
 104         return (*DMemGlobalState.pfnCheckPtr)(ptr, size);
 105     }
 106     return ptr != NULL;
 107 }
 108 
 109 /**************************************************/
 110 
 111 /*************************************************
 112  * Debug Memory Manager implementation
 113  */
 114 
 115 static MemoryListLink * DMem_TrackBlock(MemoryBlockHeader * header) {
 116     MemoryListLink *    link;
 117 
 118     link = (MemoryListLink *)DMem_ClientAllocate(sizeof(MemoryListLink));
 119     if (link != NULL) {
 120         link->header = header;
 121         link->header->listEnter = link;
 122         link->next = MemoryList.next;
 123         link->freed = FALSE;
 124         MemoryList.next = link;
 125     }
 126 
 127     return link;
 128 }
 129 
 130 static int DMem_VerifyGuardArea(const byte_t * area) {
 131     int         nbyte;
 132 
 133     for ( nbyte = 0; nbyte < MAX_GUARD_BYTES; nbyte++ ) {
 134         if (area[nbyte] != ByteGuard) {
 135             return FALSE;
 136         }
 137     }
 138     return TRUE;
 139 }
 140 
 141 static void DMem_VerifyHeader(MemoryBlockHeader * header) {
 142     DASSERTMSG( DMem_ClientCheckPtr(header, sizeof(MemoryBlockHeader)), "Invalid header" );
 143     DASSERTMSG( DMem_VerifyGuardArea(header->guard), "Header corruption, possible underwrite" );
 144     DASSERTMSG( header->linenumber > 0 && header->linenumber < MAX_LINENUM, "Header corruption, bad line number" );
 145     DASSERTMSG( header->size <= DMemGlobalState.biggestBlock, "Header corruption, block size is too large");
 146     DASSERTMSG( header->order <= DMemGlobalState.totalAllocs, "Header corruption, block order out of range");
 147 }
 148 
 149 static void DMem_VerifyTail(MemoryBlockTail * tail) {
 150     DASSERTMSG( DMem_ClientCheckPtr(tail, sizeof(MemoryBlockTail)), "Tail corruption, invalid pointer");
 151     DASSERTMSG( DMem_VerifyGuardArea(tail->guard), "Tail corruption, possible overwrite" );
 152 }
 153 
 154 static MemoryBlockHeader * DMem_VerifyBlock(void * memptr) {
 155     MemoryBlockHeader * header;
 156     MemoryBlockTail *   tail;
 157 
 158     /* check if the pointer is valid */
 159     DASSERTMSG( DMem_ClientCheckPtr(memptr, 1), "Invalid pointer");
 160 
 161     /* check if the block header is valid */
 162     header = (MemoryBlockHeader *)((byte_t *)memptr - sizeof(MemoryBlockHeader));
 163     DMem_VerifyHeader(header);
 164     /* check that the memory itself is valid */
 165     DASSERTMSG( DMem_ClientCheckPtr(memptr, DMEM_MIN(MAX_CHECK_BYTES,header->size)), "Block memory invalid" );
 166     /* check that the pointer to the alloc list is valid */
 167     DASSERTMSG( DMem_ClientCheckPtr(header->listEnter, sizeof(MemoryListLink)), "Header corruption, alloc list pointer invalid" );
 168     /* check the tail of the block for overruns */
 169     tail = (MemoryBlockTail *) ( (byte_t *)memptr + header->size );
 170     DMem_VerifyTail(tail);
 171 
 172     return header;
 173 }
 174 
 175 static MemoryBlockHeader * DMem_GetHeader(void * memptr) {
 176     MemoryBlockHeader * header = DMem_VerifyBlock(memptr);
 177     return header;
 178 }
 179 
 180 /*
 181  * Should be called before any other DMem_XXX function
 182  */
 183 void DMem_Initialize() {
 184     DMemMutex = DMutex_Create();
 185     DMutex_Enter(DMemMutex);
 186     DMemGlobalState.pfnAlloc = NULL;
 187     DMemGlobalState.pfnFree = NULL;
 188     DMemGlobalState.pfnCheckPtr = NULL;
 189     DMemGlobalState.biggestBlock = 0;
 190     DMemGlobalState.maxHeap = INT_MAX;
 191     DMemGlobalState.totalHeapUsed = 0;
 192     DMemGlobalState.failNextAlloc = FALSE;
 193     DMemGlobalState.totalAllocs = 0;
 194     DMutex_Exit(DMemMutex);
 195 }
 196 
 197 void DMem_Shutdown() {
 198     DMutex_Destroy(DMemMutex);
 199 }
 200 /*
 201  * Allocates a block of memory, reserving extra space at the start and end of the
 202  * block to store debug info on where the block was allocated, it's size, and
 203  * 'guard' areas to catch overwrite/underwrite bugs
 204  */
 205 void * DMem_AllocateBlock(size_t size, const char * filename, int linenumber) {
 206     MemoryBlockHeader * header;
 207     MemoryBlockTail *   tail;
 208     size_t              debugBlockSize;
 209     byte_t *            memptr = NULL;
 210 
 211     DMutex_Enter(DMemMutex);
 212     if (DMemGlobalState.failNextAlloc) {
 213     /* force an allocation failure if so ordered */
 214         DMemGlobalState.failNextAlloc = FALSE; /* reset flag */
 215         goto Exit;
 216     }
 217 
 218     /* allocate a block large enough to hold extra debug info */
 219     debugBlockSize = sizeof(MemoryBlockHeader) + size + sizeof(MemoryBlockTail);
 220     header = (MemoryBlockHeader *)DMem_ClientAllocate(debugBlockSize);
 221     if (header == NULL) {
 222         goto Exit;
 223     }
 224 
 225     /* add block to list of allocated memory */
 226     header->listEnter = DMem_TrackBlock(header);
 227     if ( header->listEnter == NULL ) {
 228         goto Exit;
 229     }
 230 
 231     /* store size of requested block */
 232     header->size = size;
 233     /* update maximum block size */
 234     DMemGlobalState.biggestBlock = DMEM_MAX(header->size, DMemGlobalState.biggestBlock);
 235     /* update used memory total */
 236     DMemGlobalState.totalHeapUsed += header->size;
 237     /* store filename and linenumber where allocation routine was called */
 238     strncpy(header->filename, filename, FILENAME_MAX);
 239     header->linenumber = linenumber;
 240     /* store the order the block was allocated in */
 241     header->order = DMemGlobalState.totalAllocs++;
 242     /* initialize memory to a recognizable 'inited' value */
 243     memptr = (byte_t *)header + sizeof(MemoryBlockHeader);
 244     memset(memptr, ByteInited, size);
 245     /* put guard area before block */
 246     memset(header->guard, ByteGuard, MAX_GUARD_BYTES);
 247     /* put guard area after block */
 248     tail = (MemoryBlockTail *)(memptr + size);
 249     memset(tail->guard, ByteGuard, MAX_GUARD_BYTES);
 250 
 251 Exit:
 252     DMutex_Exit(DMemMutex);
 253     return memptr;
 254 }
 255 
 256 /*
 257  * Frees block of memory allocated with DMem_AllocateBlock
 258  */
 259 void DMem_FreeBlock(void * memptr) {
 260     MemoryBlockHeader * header;
 261 
 262     DMutex_Enter(DMemMutex);
 263     if ( memptr == NULL) {
 264         goto Exit;
 265     }
 266 
 267     /* get the debug block header preceding the allocated memory */
 268     header = DMem_GetHeader(memptr);
 269     /* fill memory with recognizable 'freed' value */
 270     memset(memptr, ByteFreed, header->size);
 271     /* mark block as freed */
 272     header->listEnter->freed = TRUE;
 273     /* update used memory total */
 274     DMemGlobalState.totalHeapUsed -= header->size;
 275 Exit:
 276     DMutex_Exit(DMemMutex);
 277 }
 278 
 279 static void DMem_DumpHeader(MemoryBlockHeader * header) {
 280     char        report[FILENAME_MAX+MAX_DECIMAL_DIGITS*3+1];
 281 
 282     DMem_VerifyHeader(header);
 283     sprintf(report, "file: %s, line %d\n" \
 284                     "size: %d bytes\n" \
 285                     "order: %d\n" \
 286                     "-------",
 287                     header->filename, (int)header->linenumber, (int)header->size, header->order);
 288     DTRACE_PRINTLN(report);
 289 }
 290 
 291 /*
 292  * Call this function at shutdown time to report any leaked blocks
 293  */
 294 void DMem_ReportLeaks() {
 295     MemoryListLink *    link;
 296 
 297     DMutex_Enter(DMemMutex);
 298 
 299     /* Force memory leaks to be output regardless of trace settings */
 300     DTrace_EnableFile(THIS_FILE, TRUE);
 301     DTRACE_PRINTLN("--------------------------");
 302     DTRACE_PRINTLN("Debug Memory Manager Leaks");
 303     DTRACE_PRINTLN("--------------------------");
 304 
 305     /* walk through allocated list and dump any blocks not marked as freed */
 306     link = MemoryList.next;
 307     while (link != NULL) {
 308         if ( !link->freed ) {
 309             DMem_DumpHeader(link->header);
 310         }
 311         link = link->next;
 312     }
 313 
 314     DMutex_Exit(DMemMutex);
 315 }
 316 
 317 void DMem_SetAllocCallback( DMEM_ALLOCFN pfn ) {
 318     DMutex_Enter(DMemMutex);
 319     DMemGlobalState.pfnAlloc = pfn;
 320     DMutex_Exit(DMemMutex);
 321 }
 322 
 323 void DMem_SetFreeCallback( DMEM_FREEFN pfn ) {
 324     DMutex_Enter(DMemMutex);
 325     DMemGlobalState.pfnFree = pfn;
 326     DMutex_Exit(DMemMutex);
 327 }
 328 
 329 void DMem_SetCheckPtrCallback( DMEM_CHECKPTRFN pfn ) {
 330     DMutex_Enter(DMemMutex);
 331     DMemGlobalState.pfnCheckPtr = pfn;
 332     DMutex_Exit(DMemMutex);
 333 }
 334 
 335 void DMem_DisableMutex() {
 336     DMemMutex = NULL;
 337 }
 338 
 339 #endif  /* defined(DEBUG) */
 340 
 341 /* The following line is only here to prevent compiler warnings
 342  * on release (non-debug) builds
 343  */
 344 static int dummyVariable = 0;