blob: e8acf238942a4512cc8c627e223e7ef831dedc79 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2002 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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
33typedef char byte_t;
34
35static const byte_t ByteInited = '\xCD';
36static const byte_t ByteFreed = '\xDD';
37static const byte_t ByteGuard = '\xFD';
38
39enum {
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 */
47typedef 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 */
57typedef struct MemoryBlockTail {
58 byte_t guard[MAX_GUARD_BYTES]; /* guard area overrun check */
59} MemoryBlockTail;
60
61/* Linked list of allocated memory blocks */
62typedef struct MemoryListLink {
63 struct MemoryListLink * next;
64 MemoryBlockHeader * header;
65 int freed;
66} MemoryListLink;
67
68/**************************************************
69 * Global Data structures
70 */
71static DMemState DMemGlobalState;
72extern const DMemState * DMemStatePtr = &DMemGlobalState;
73static MemoryListLink MemoryList = {NULL,NULL,FALSE};
74static dmutex_t DMemMutex = NULL;
75
76/**************************************************/
77
78/*************************************************
79 * Client callback invocation functions
80 */
81static void * DMem_ClientAllocate(size_t size) {
82 if (DMemGlobalState.pfnAlloc != NULL) {
83 return (*DMemGlobalState.pfnAlloc)(size);
84 }
85 return malloc(size);
86}
87
88static void DMem_ClientFree(void * ptr) {
89 if (DMemGlobalState.pfnFree != NULL) {
90 (*DMemGlobalState.pfnFree)(ptr);
91 }
92 free(ptr);
93}
94
95static 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
108static 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
123static 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
134static 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
142static 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
147static 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
168static 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 */
176void 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
190void 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 */
198void * 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 goto Exit;
222 }
223
224 /* store size of requested block */
225 header->size = size;
226 /* update maximum block size */
227 DMemGlobalState.biggestBlock = DMEM_MAX(header->size, DMemGlobalState.biggestBlock);
228 /* update used memory total */
229 DMemGlobalState.totalHeapUsed += header->size;
230 /* store filename and linenumber where allocation routine was called */
231 strncpy(header->filename, filename, FILENAME_MAX);
232 header->linenumber = linenumber;
233 /* store the order the block was allocated in */
234 header->order = DMemGlobalState.totalAllocs++;
235 /* initialize memory to a recognizable 'inited' value */
236 memptr = (byte_t *)header + sizeof(MemoryBlockHeader);
237 memset(memptr, ByteInited, size);
238 /* put guard area before block */
239 memset(header->guard, ByteGuard, MAX_GUARD_BYTES);
240 /* put guard area after block */
241 tail = (MemoryBlockTail *)(memptr + size);
242 memset(tail->guard, ByteGuard, MAX_GUARD_BYTES);
243
244Exit:
245 DMutex_Exit(DMemMutex);
246 return memptr;
247}
248
249/*
250 * Frees block of memory allocated with DMem_AllocateBlock
251 */
252void DMem_FreeBlock(void * memptr) {
253 MemoryBlockHeader * header;
254
255 DMutex_Enter(DMemMutex);
256 if ( memptr == NULL) {
257 goto Exit;
258 }
259
260 /* get the debug block header preceding the allocated memory */
261 header = DMem_GetHeader(memptr);
262 /* fill memory with recognizable 'freed' value */
263 memset(memptr, ByteFreed, header->size);
264 /* mark block as freed */
265 header->listEnter->freed = TRUE;
266 /* update used memory total */
267 DMemGlobalState.totalHeapUsed -= header->size;
268Exit:
269 DMutex_Exit(DMemMutex);
270}
271
272static void DMem_DumpHeader(MemoryBlockHeader * header) {
273 char report[FILENAME_MAX+MAX_DECIMAL_DIGITS*3+1];
274 static const char * reportFormat =
275 "file: %s, line %d\n"
276 "size: %d bytes\n"
277 "order: %d\n"
278 "-------";
279
280 DMem_VerifyHeader(header);
281 sprintf(report, reportFormat, header->filename, header->linenumber, header->size, header->order);
282 DTRACE_PRINTLN(report);
283}
284
285/*
286 * Call this function at shutdown time to report any leaked blocks
287 */
288void DMem_ReportLeaks() {
289 MemoryListLink * link;
290
291 DMutex_Enter(DMemMutex);
292
293 /* Force memory leaks to be output regardless of trace settings */
294 DTrace_EnableFile(__FILE__, TRUE);
295 DTRACE_PRINTLN("--------------------------");
296 DTRACE_PRINTLN("Debug Memory Manager Leaks");
297 DTRACE_PRINTLN("--------------------------");
298
299 /* walk through allocated list and dump any blocks not marked as freed */
300 link = MemoryList.next;
301 while (link != NULL) {
302 if ( !link->freed ) {
303 DMem_DumpHeader(link->header);
304 }
305 link = link->next;
306 }
307
308 DMutex_Exit(DMemMutex);
309}
310
311void DMem_SetAllocCallback( DMEM_ALLOCFN pfn ) {
312 DMutex_Enter(DMemMutex);
313 DMemGlobalState.pfnAlloc = pfn;
314 DMutex_Exit(DMemMutex);
315}
316
317void DMem_SetFreeCallback( DMEM_FREEFN pfn ) {
318 DMutex_Enter(DMemMutex);
319 DMemGlobalState.pfnFree = pfn;
320 DMutex_Exit(DMemMutex);
321}
322
323void DMem_SetCheckPtrCallback( DMEM_CHECKPTRFN pfn ) {
324 DMutex_Enter(DMemMutex);
325 DMemGlobalState.pfnCheckPtr = pfn;
326 DMutex_Exit(DMemMutex);
327}
328
329void DMem_DisableMutex() {
330 DMemMutex = NULL;
331}
332
333#endif /* defined(DEBUG) */
334
335/* The following line is only here to prevent compiler warnings
336 * on release (non-debug) builds
337 */
338static int dummyVariable = 0;