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