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 = &DMemGlobalState; 78 static MemoryListLink MemoryList = {NULL,NULL,FALSE}; 79 static dmutex_t DMemMutex = NULL; 80 81 /**************************************************/ 82 83 /************************************************* 84 * Client callback invocation functions 85 */ 86 static void * DMem_ClientAllocate(size_t size) { 87 if (DMemGlobalState.pfnAlloc != NULL) { 88 return (*DMemGlobalState.pfnAlloc)(size); 89 } 90 return malloc(size); 91 } 92 93 static void DMem_ClientFree(void * ptr) { 94 if (DMemGlobalState.pfnFree != NULL) { 95 (*DMemGlobalState.pfnFree)(ptr); 96 } 97 free(ptr); 98 } 99 100 static dbool_t DMem_ClientCheckPtr(void * ptr, size_t size) { 101 if (DMemGlobalState.pfnCheckPtr != NULL) { 102 return (*DMemGlobalState.pfnCheckPtr)(ptr, size); 103 } 104 return ptr != NULL; 105 } 106 107 /**************************************************/ 108 109 /************************************************* 110 * Debug Memory Manager implementation 111 */ 112 113 static MemoryListLink * DMem_TrackBlock(MemoryBlockHeader * header) { 114 MemoryListLink * link; 115 116 link = (MemoryListLink *)DMem_ClientAllocate(sizeof(MemoryListLink)); 117 if (link != NULL) { 118 link->header = header; 119 link->header->listEnter = link; 120 link->next = MemoryList.next; 121 link->freed = FALSE; 122 MemoryList.next = link; 123 } 124 125 return link; 126 } 127 128 static int DMem_VerifyGuardArea(const byte_t * area) { 129 int nbyte; 130 131 for ( nbyte = 0; nbyte < MAX_GUARD_BYTES; nbyte++ ) { 132 if (area[nbyte] != ByteGuard) { 133 return FALSE; 134 } 135 } 136 return TRUE; 137 } 138 139 static void DMem_VerifyHeader(MemoryBlockHeader * header) { 140 DASSERTMSG( DMem_ClientCheckPtr(header, sizeof(MemoryBlockHeader)), "Invalid header" ); 141 DASSERTMSG( DMem_VerifyGuardArea(header->guard), "Header corruption, possible underwrite" ); 142 DASSERTMSG( header->linenumber > 0 && header->linenumber < MAX_LINENUM, "Header corruption, bad line number" ); 143 DASSERTMSG( header->size <= DMemGlobalState.biggestBlock, "Header corruption, block size is too large"); 144 DASSERTMSG( header->order <= DMemGlobalState.totalAllocs, "Header corruption, block order out of range"); 145 } 146 147 static void DMem_VerifyTail(MemoryBlockTail * tail) { 148 DASSERTMSG( DMem_ClientCheckPtr(tail, sizeof(MemoryBlockTail)), "Tail corruption, invalid pointer"); 149 DASSERTMSG( DMem_VerifyGuardArea(tail->guard), "Tail corruption, possible overwrite" ); 150 } 151 152 static MemoryBlockHeader * DMem_VerifyBlock(void * memptr) { 153 MemoryBlockHeader * header; 154 MemoryBlockTail * tail; 155 156 /* check if the pointer is valid */ 157 DASSERTMSG( DMem_ClientCheckPtr(memptr, 1), "Invalid pointer"); 158 159 /* check if the block header is valid */ 160 header = (MemoryBlockHeader *)((byte_t *)memptr - sizeof(MemoryBlockHeader)); 161 DMem_VerifyHeader(header); 162 /* check that the memory itself is valid */ 163 DASSERTMSG( DMem_ClientCheckPtr(memptr, DMEM_MIN(MAX_CHECK_BYTES,header->size)), "Block memory invalid" ); 164 /* check that the pointer to the alloc list is valid */ 165 DASSERTMSG( DMem_ClientCheckPtr(header->listEnter, sizeof(MemoryListLink)), "Header corruption, alloc list pointer invalid" ); 166 /* check the tail of the block for overruns */ 167 tail = (MemoryBlockTail *) ( (byte_t *)memptr + header->size ); 168 DMem_VerifyTail(tail); 169 170 return header; 171 } 172 173 static MemoryBlockHeader * DMem_GetHeader(void * memptr) { 174 MemoryBlockHeader * header = DMem_VerifyBlock(memptr); 175 return header; 176 } 177 178 /* 179 * Should be called before any other DMem_XXX function 180 */ 181 void DMem_Initialize() { 182 DMemMutex = DMutex_Create(); 183 DMutex_Enter(DMemMutex); 184 DMemGlobalState.pfnAlloc = NULL; 185 DMemGlobalState.pfnFree = NULL; 186 DMemGlobalState.pfnCheckPtr = NULL; 187 DMemGlobalState.biggestBlock = 0; 188 DMemGlobalState.maxHeap = INT_MAX; 189 DMemGlobalState.totalHeapUsed = 0; 190 DMemGlobalState.failNextAlloc = FALSE; 191 DMemGlobalState.totalAllocs = 0; 192 DMutex_Exit(DMemMutex); 193 } 194 195 void DMem_Shutdown() { 196 DMutex_Destroy(DMemMutex); 197 } 198 /* 199 * Allocates a block of memory, reserving extra space at the start and end of the 200 * block to store debug info on where the block was allocated, it's size, and 201 * 'guard' areas to catch overwrite/underwrite bugs 202 */ 203 void * DMem_AllocateBlock(size_t size, const char * filename, int linenumber) { 204 MemoryBlockHeader * header; 205 MemoryBlockTail * tail; 206 size_t debugBlockSize; 207 byte_t * memptr = NULL; 208 209 DMutex_Enter(DMemMutex); 210 if (DMemGlobalState.failNextAlloc) { 211 /* force an allocation failure if so ordered */ 212 DMemGlobalState.failNextAlloc = FALSE; /* reset flag */ 213 goto Exit; 214 } 215 216 /* allocate a block large enough to hold extra debug info */ 217 debugBlockSize = sizeof(MemoryBlockHeader) + size + sizeof(MemoryBlockTail); 218 header = (MemoryBlockHeader *)DMem_ClientAllocate(debugBlockSize); 219 if (header == NULL) { 220 goto Exit; 221 } 222 223 /* add block to list of allocated memory */ 224 header->listEnter = DMem_TrackBlock(header); 225 if ( header->listEnter == NULL ) { 226 goto Exit; 227 } 228 229 /* store size of requested block */ 230 header->size = size; 231 /* update maximum block size */ 232 DMemGlobalState.biggestBlock = DMEM_MAX(header->size, DMemGlobalState.biggestBlock); 233 /* update used memory total */ 234 DMemGlobalState.totalHeapUsed += header->size; 235 /* store filename and linenumber where allocation routine was called */ 236 strncpy(header->filename, filename, FILENAME_MAX); 237 header->linenumber = linenumber; 238 /* store the order the block was allocated in */ 239 header->order = DMemGlobalState.totalAllocs++; 240 /* initialize memory to a recognizable 'inited' value */ 241 memptr = (byte_t *)header + sizeof(MemoryBlockHeader); 242 memset(memptr, ByteInited, size); 243 /* put guard area before block */ 244 memset(header->guard, ByteGuard, MAX_GUARD_BYTES); 245 /* put guard area after block */ 246 tail = (MemoryBlockTail *)(memptr + size); 247 memset(tail->guard, ByteGuard, MAX_GUARD_BYTES); 248 249 Exit: 250 DMutex_Exit(DMemMutex); 251 return memptr; 252 } 253 254 /* 255 * Frees block of memory allocated with DMem_AllocateBlock 256 */ 257 void DMem_FreeBlock(void * memptr) { 258 MemoryBlockHeader * header; 259 260 DMutex_Enter(DMemMutex); 261 if ( memptr == NULL) { 262 goto Exit; 263 } 264 265 /* get the debug block header preceding the allocated memory */ 266 header = DMem_GetHeader(memptr); 267 /* fill memory with recognizable 'freed' value */ 268 memset(memptr, ByteFreed, header->size); 269 /* mark block as freed */ 270 header->listEnter->freed = TRUE; 271 /* update used memory total */ 272 DMemGlobalState.totalHeapUsed -= header->size; 273 Exit: 274 DMutex_Exit(DMemMutex); 275 } 276 277 static void DMem_DumpHeader(MemoryBlockHeader * header) { 278 char report[FILENAME_MAX+MAX_DECIMAL_DIGITS*3+1]; 279 static const char * reportFormat = 280 "file: %s, line %d\n" 281 "size: %d bytes\n" 282 "order: %d\n" 283 "-------"; 284 285 DMem_VerifyHeader(header); 286 sprintf(report, reportFormat, header->filename, header->linenumber, 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;