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