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
38typedef char byte_t;
39
40static const byte_t ByteInited = '\xCD';
41static const byte_t ByteFreed = '\xDD';
42static const byte_t ByteGuard = '\xFD';
43
44enum {
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 */
52typedef 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 */
62typedef struct MemoryBlockTail {
63 byte_t guard[MAX_GUARD_BYTES]; /* guard area overrun check */
64} MemoryBlockTail;
65
66/* Linked list of allocated memory blocks */
67typedef struct MemoryListLink {
68 struct MemoryListLink * next;
69 MemoryBlockHeader * header;
70 int freed;
71} MemoryListLink;
72
73/**************************************************
74 * Global Data structures
75 */
76static DMemState DMemGlobalState;
77extern const DMemState * DMemStatePtr = &DMemGlobalState;
78static MemoryListLink MemoryList = {NULL,NULL,FALSE};
79static dmutex_t DMemMutex = NULL;
80
81/**************************************************/
82
83/*************************************************
84 * Client callback invocation functions
85 */
86static void * DMem_ClientAllocate(size_t size) {
87 if (DMemGlobalState.pfnAlloc != NULL) {
88 return (*DMemGlobalState.pfnAlloc)(size);
89 }
90 return malloc(size);
91}
92
93static void DMem_ClientFree(void * ptr) {
94 if (DMemGlobalState.pfnFree != NULL) {
95 (*DMemGlobalState.pfnFree)(ptr);
96 }
97 free(ptr);
98}
99
100static 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
113static 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
128static 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
139static 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
147static 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
152static 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
173static 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 */
181void 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
195void 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 */
203void * 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
249Exit:
250 DMutex_Exit(DMemMutex);
251 return memptr;
252}
253
254/*
255 * Frees block of memory allocated with DMem_AllocateBlock
256 */
257void 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;
273Exit:
274 DMutex_Exit(DMemMutex);
275}
276
277static 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 */
293void 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
316void DMem_SetAllocCallback( DMEM_ALLOCFN pfn ) {
317 DMutex_Enter(DMemMutex);
318 DMemGlobalState.pfnAlloc = pfn;
319 DMutex_Exit(DMemMutex);
320}
321
322void DMem_SetFreeCallback( DMEM_FREEFN pfn ) {
323 DMutex_Enter(DMemMutex);
324 DMemGlobalState.pfnFree = pfn;
325 DMutex_Exit(DMemMutex);
326}
327
328void DMem_SetCheckPtrCallback( DMEM_CHECKPTRFN pfn ) {
329 DMutex_Enter(DMemMutex);
330 DMemGlobalState.pfnCheckPtr = pfn;
331 DMutex_Exit(DMemMutex);
332}
333
334void 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 */
343static int dummyVariable = 0;
344