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 | */ |
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 | |
93 | static void DMem_ClientFree(void * ptr) { |
94 | if (DMemGlobalState.pfnFree != NULL) { |
95 | (*DMemGlobalState.pfnFree)(ptr); |
96 | } |
97 | free(ptr); |
98 | } |
99 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | */ |
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 | |
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 | */ |
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 | */ |
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 | |
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 | */ |
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 | |
316 | void DMem_SetAllocCallback( DMEM_ALLOCFN pfn ) { |
317 | DMutex_Enter(DMemMutex); |
318 | DMemGlobalState.pfnAlloc = pfn; |
319 | DMutex_Exit(DMemMutex); |
320 | } |
321 | |
322 | void DMem_SetFreeCallback( DMEM_FREEFN pfn ) { |
323 | DMutex_Enter(DMemMutex); |
324 | DMemGlobalState.pfnFree = pfn; |
325 | DMutex_Exit(DMemMutex); |
326 | } |
327 | |
328 | void DMem_SetCheckPtrCallback( DMEM_CHECKPTRFN pfn ) { |
329 | DMutex_Enter(DMemMutex); |
330 | DMemGlobalState.pfnCheckPtr = pfn; |
331 | DMutex_Exit(DMemMutex); |
332 | } |
333 | |
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 | |