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