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 const DMemState* DMemStatePtr = &DMemGlobalState; //initialize separately to keep GCC happy 79 static MemoryListLink MemoryList = {NULL,NULL,FALSE}; 80 static dmutex_t DMemMutex = NULL; 81 82 /**************************************************/ 83 84 /************************************************* 85 * Client callback invocation functions 86 */ 87 static void * DMem_ClientAllocate(size_t size) { 88 if (DMemGlobalState.pfnAlloc != NULL) { 89 return (*DMemGlobalState.pfnAlloc)(size); 90 } 91 return malloc(size); 92 } 93 94 static void DMem_ClientFree(void * ptr) { 95 if (DMemGlobalState.pfnFree != NULL) { 96 (*DMemGlobalState.pfnFree)(ptr); 97 } 98 free(ptr); 99 } 100 101 static dbool_t DMem_ClientCheckPtr(void * ptr, size_t size) { 102 if (DMemGlobalState.pfnCheckPtr != NULL) { 103 return (*DMemGlobalState.pfnCheckPtr)(ptr, size); 104 } 105 return ptr != NULL; 106 } 107 108 /**************************************************/ 109 110 /************************************************* 111 * Debug Memory Manager implementation 112 */ 113 114 static MemoryListLink * DMem_TrackBlock(MemoryBlockHeader * header) { 115 MemoryListLink * link; 116 117 link = (MemoryListLink *)DMem_ClientAllocate(sizeof(MemoryListLink)); 118 if (link != NULL) { 119 link->header = header; 120 link->header->listEnter = link; 121 link->next = MemoryList.next; 122 link->freed = FALSE; 123 MemoryList.next = link; 124 } 125 126 return link; 127 } 128 129 static int DMem_VerifyGuardArea(const byte_t * area) { 130 int nbyte; 131 132 for ( nbyte = 0; nbyte < MAX_GUARD_BYTES; nbyte++ ) { 133 if (area[nbyte] != ByteGuard) { 134 return FALSE; 135 } 136 } 137 return TRUE; 138 } 139 140 static void DMem_VerifyHeader(MemoryBlockHeader * header) { 141 DASSERTMSG( DMem_ClientCheckPtr(header, sizeof(MemoryBlockHeader)), "Invalid header" ); 142 DASSERTMSG( DMem_VerifyGuardArea(header->guard), "Header corruption, possible underwrite" ); 143 DASSERTMSG( header->linenumber > 0 && header->linenumber < MAX_LINENUM, "Header corruption, bad line number" ); 144 DASSERTMSG( header->size <= DMemGlobalState.biggestBlock, "Header corruption, block size is too large"); 145 DASSERTMSG( header->order <= DMemGlobalState.totalAllocs, "Header corruption, block order out of range"); 146 } 147 148 static void DMem_VerifyTail(MemoryBlockTail * tail) { 149 DASSERTMSG( DMem_ClientCheckPtr(tail, sizeof(MemoryBlockTail)), "Tail corruption, invalid pointer"); 150 DASSERTMSG( DMem_VerifyGuardArea(tail->guard), "Tail corruption, possible overwrite" ); 151 } 152 153 static MemoryBlockHeader * DMem_VerifyBlock(void * memptr) { 154 MemoryBlockHeader * header; 155 MemoryBlockTail * tail; 156 157 /* check if the pointer is valid */ 158 DASSERTMSG( DMem_ClientCheckPtr(memptr, 1), "Invalid pointer"); 159 160 /* check if the block header is valid */ 161 header = (MemoryBlockHeader *)((byte_t *)memptr - sizeof(MemoryBlockHeader)); 162 DMem_VerifyHeader(header); 163 /* check that the memory itself is valid */ 164 DASSERTMSG( DMem_ClientCheckPtr(memptr, DMEM_MIN(MAX_CHECK_BYTES,header->size)), "Block memory invalid" ); 165 /* check that the pointer to the alloc list is valid */ 166 DASSERTMSG( DMem_ClientCheckPtr(header->listEnter, sizeof(MemoryListLink)), "Header corruption, alloc list pointer invalid" ); 167 /* check the tail of the block for overruns */ 168 tail = (MemoryBlockTail *) ( (byte_t *)memptr + header->size ); 169 DMem_VerifyTail(tail); 170 171 return header; 172 } 173 174 static MemoryBlockHeader * DMem_GetHeader(void * memptr) { 175 MemoryBlockHeader * header = DMem_VerifyBlock(memptr); 176 return header; 177 } 178 179 /* 180 * Should be called before any other DMem_XXX function 181 */ 182 void DMem_Initialize() { 183 DMemMutex = DMutex_Create(); 184 DMutex_Enter(DMemMutex); 185 DMemGlobalState.pfnAlloc = NULL; 186 DMemGlobalState.pfnFree = NULL; 187 DMemGlobalState.pfnCheckPtr = NULL; 188 DMemGlobalState.biggestBlock = 0; 189 DMemGlobalState.maxHeap = INT_MAX; 190 DMemGlobalState.totalHeapUsed = 0; 191 DMemGlobalState.failNextAlloc = FALSE; 192 DMemGlobalState.totalAllocs = 0; 193 DMutex_Exit(DMemMutex); 194 } 195 196 void DMem_Shutdown() { 197 DMutex_Destroy(DMemMutex); 198 } 199 /* 200 * Allocates a block of memory, reserving extra space at the start and end of the 201 * block to store debug info on where the block was allocated, it's size, and 202 * 'guard' areas to catch overwrite/underwrite bugs 203 */ 204 void * DMem_AllocateBlock(size_t size, const char * filename, int linenumber) { 205 MemoryBlockHeader * header; 206 MemoryBlockTail * tail; 207 size_t debugBlockSize; 208 byte_t * memptr = NULL; 209 210 DMutex_Enter(DMemMutex); 211 if (DMemGlobalState.failNextAlloc) { 212 /* force an allocation failure if so ordered */ 213 DMemGlobalState.failNextAlloc = FALSE; /* reset flag */ 214 goto Exit; 215 } 216 217 /* allocate a block large enough to hold extra debug info */ 218 debugBlockSize = sizeof(MemoryBlockHeader) + size + sizeof(MemoryBlockTail); 219 header = (MemoryBlockHeader *)DMem_ClientAllocate(debugBlockSize); 220 if (header == NULL) { 221 goto Exit; 222 } 223 224 /* add block to list of allocated memory */ 225 header->listEnter = DMem_TrackBlock(header); 226 if ( header->listEnter == NULL ) { 227 goto Exit; 228 } 229 230 /* store size of requested block */ 231 header->size = size; 232 /* update maximum block size */ 233 DMemGlobalState.biggestBlock = DMEM_MAX(header->size, DMemGlobalState.biggestBlock); 234 /* update used memory total */ 235 DMemGlobalState.totalHeapUsed += header->size; 236 /* store filename and linenumber where allocation routine was called */ 237 strncpy(header->filename, filename, FILENAME_MAX); 238 header->linenumber = linenumber; 239 /* store the order the block was allocated in */ 240 header->order = DMemGlobalState.totalAllocs++; 241 /* initialize memory to a recognizable 'inited' value */ 242 memptr = (byte_t *)header + sizeof(MemoryBlockHeader); 243 memset(memptr, ByteInited, size); 244 /* put guard area before block */ 245 memset(header->guard, ByteGuard, MAX_GUARD_BYTES); 246 /* put guard area after block */ 247 tail = (MemoryBlockTail *)(memptr + size); 248 memset(tail->guard, ByteGuard, MAX_GUARD_BYTES); 249 250 Exit: 251 DMutex_Exit(DMemMutex); 252 return memptr; 253 } 254 255 /* 256 * Frees block of memory allocated with DMem_AllocateBlock 257 */ 258 void DMem_FreeBlock(void * memptr) { 259 MemoryBlockHeader * header; 260 261 DMutex_Enter(DMemMutex); 262 if ( memptr == NULL) { 263 goto Exit; 264 } 265 266 /* get the debug block header preceding the allocated memory */ 267 header = DMem_GetHeader(memptr); 268 /* fill memory with recognizable 'freed' value */ 269 memset(memptr, ByteFreed, header->size); 270 /* mark block as freed */ 271 header->listEnter->freed = TRUE; 272 /* update used memory total */ 273 DMemGlobalState.totalHeapUsed -= header->size; 274 Exit: 275 DMutex_Exit(DMemMutex); 276 } 277 278 static void DMem_DumpHeader(MemoryBlockHeader * header) { 279 char report[FILENAME_MAX+MAX_DECIMAL_DIGITS*3+1+8]; 280 281 DMem_VerifyHeader(header); 282 sprintf(report, "file: %s, line %d\n"\ 283 "size: %d bytes\n"\ 284 "order: %d\n"\ 285 "-------", 286 header->filename, (int)header->linenumber, (int)header->size, header->order); 287 DTRACE_PRINTLN(report); 288 } 289 290 /* 291 * Call this function at shutdown time to report any leaked blocks 292 */ 293 void DMem_ReportLeaks() { 294 MemoryListLink * link; 295 296 DMutex_Enter(DMemMutex); 297 298 /* Force memory leaks to be output regardless of trace settings */ 299 DTrace_EnableFile(THIS_FILE, TRUE); 300 DTRACE_PRINTLN("--------------------------"); 301 DTRACE_PRINTLN("Debug Memory Manager Leaks"); 302 DTRACE_PRINTLN("--------------------------"); 303 304 /* walk through allocated list and dump any blocks not marked as freed */ 305 link = MemoryList.next; 306 while (link != NULL) { 307 if ( !link->freed ) { 308 DMem_DumpHeader(link->header); 309 } 310 link = link->next; 311 } 312 313 DMutex_Exit(DMemMutex); 314 } 315 316 void DMem_SetAllocCallback( DMEM_ALLOCFN pfn ) { 317 DMutex_Enter(DMemMutex); 318 DMemGlobalState.pfnAlloc = pfn; 319 DMutex_Exit(DMemMutex); 320 } 321 322 void DMem_SetFreeCallback( DMEM_FREEFN pfn ) { 323 DMutex_Enter(DMemMutex); 324 DMemGlobalState.pfnFree = pfn; 325 DMutex_Exit(DMemMutex); 326 } 327 328 void DMem_SetCheckPtrCallback( DMEM_CHECKPTRFN pfn ) { 329 DMutex_Enter(DMemMutex); 330 DMemGlobalState.pfnCheckPtr = pfn; 331 DMutex_Exit(DMemMutex); 332 } 333 334 void DMem_DisableMutex() { 335 DMemMutex = NULL; 336 } 337 338 #endif /* defined(DEBUG) */ 339 340 /* The following line is only here to prevent compiler warnings 341 * on release (non-debug) builds 342 */ 343 static int dummyVariable = 0;