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;