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  */
DMem_ClientAllocate(size_t size)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 
DMem_ClientFree(void * ptr)93 static void DMem_ClientFree(void * ptr) {
94     if (DMemGlobalState.pfnFree != NULL) {
95         (*DMemGlobalState.pfnFree)(ptr);
96     }
97     free(ptr);
98 }
99 
DMem_ClientCheckPtr(void * ptr,size_t size)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 
DMem_TrackBlock(MemoryBlockHeader * header)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 
DMem_VerifyGuardArea(const byte_t * area)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 
DMem_VerifyHeader(MemoryBlockHeader * header)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 
DMem_VerifyTail(MemoryBlockTail * tail)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 
DMem_VerifyBlock(void * memptr)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 
DMem_GetHeader(void * memptr)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  */
DMem_Initialize()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 
DMem_Shutdown()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  */
DMem_AllocateBlock(size_t size,const char * filename,int linenumber)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  */
DMem_FreeBlock(void * memptr)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 
DMem_DumpHeader(MemoryBlockHeader * header)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  */
DMem_ReportLeaks()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 
DMem_SetAllocCallback(DMEM_ALLOCFN pfn)316 void DMem_SetAllocCallback( DMEM_ALLOCFN pfn ) {
317     DMutex_Enter(DMemMutex);
318     DMemGlobalState.pfnAlloc = pfn;
319     DMutex_Exit(DMemMutex);
320 }
321 
DMem_SetFreeCallback(DMEM_FREEFN pfn)322 void DMem_SetFreeCallback( DMEM_FREEFN pfn ) {
323     DMutex_Enter(DMemMutex);
324     DMemGlobalState.pfnFree = pfn;
325     DMutex_Exit(DMemMutex);
326 }
327 
DMem_SetCheckPtrCallback(DMEM_CHECKPTRFN pfn)328 void DMem_SetCheckPtrCallback( DMEM_CHECKPTRFN pfn ) {
329     DMutex_Enter(DMemMutex);
330     DMemGlobalState.pfnCheckPtr = pfn;
331     DMutex_Exit(DMemMutex);
332 }
333 
DMem_DisableMutex()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;
344