Path: blob/master/src/java.desktop/share/native/common/awt/debug/debug_mem.c
41171 views
/*1* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#if defined(DEBUG)2627#include "debug_util.h"2829#define DMEM_MIN(a,b) (a) < (b) ? (a) : (b)30#define DMEM_MAX(a,b) (a) > (b) ? (a) : (b)3132typedef char byte_t;3334static const byte_t ByteInited = '\xCD';35static const byte_t ByteFreed = '\xDD';36static const byte_t ByteGuard = '\xFD';3738enum {39MAX_LINENUM = 50000, /* I certainly hope we don't have source files bigger than this */40MAX_CHECK_BYTES = 27, /* max bytes to check at start of block */41MAX_GUARD_BYTES = 8, /* size of guard areas on either side of a block */42MAX_DECIMAL_DIGITS = 1543};4445/* Debug Info Header to precede allocated block */46typedef struct MemoryBlockHeader {47char filename[FILENAME_MAX+1]; /* filename where alloc occurred */48int linenumber; /* line where alloc occurred */49size_t size; /* size of the allocation */50int order; /* the order the block was allocated in */51struct MemoryListLink * listEnter; /* pointer to the free list node */52byte_t guard[MAX_GUARD_BYTES]; /* guard area for underrun check */53} MemoryBlockHeader;5455/* Tail to follow allocated block */56typedef struct MemoryBlockTail {57byte_t guard[MAX_GUARD_BYTES]; /* guard area overrun check */58} MemoryBlockTail;5960/* Linked list of allocated memory blocks */61typedef struct MemoryListLink {62struct MemoryListLink * next;63MemoryBlockHeader * header;64int freed;65} MemoryListLink;6667/**************************************************68* Global Data structures69*/70static DMemState DMemGlobalState;71extern const DMemState * DMemStatePtr = &DMemGlobalState;72static MemoryListLink MemoryList = {NULL,NULL,FALSE};73static dmutex_t DMemMutex = NULL;7475/**************************************************/7677/*************************************************78* Client callback invocation functions79*/80static void * DMem_ClientAllocate(size_t size) {81if (DMemGlobalState.pfnAlloc != NULL) {82return (*DMemGlobalState.pfnAlloc)(size);83}84return malloc(size);85}8687static void DMem_ClientFree(void * ptr) {88if (DMemGlobalState.pfnFree != NULL) {89(*DMemGlobalState.pfnFree)(ptr);90}91free(ptr);92}9394static dbool_t DMem_ClientCheckPtr(void * ptr, size_t size) {95if (DMemGlobalState.pfnCheckPtr != NULL) {96return (*DMemGlobalState.pfnCheckPtr)(ptr, size);97}98return ptr != NULL;99}100101/**************************************************/102103/*************************************************104* Debug Memory Manager implementation105*/106107static MemoryListLink * DMem_TrackBlock(MemoryBlockHeader * header) {108MemoryListLink * link;109110link = (MemoryListLink *)DMem_ClientAllocate(sizeof(MemoryListLink));111if (link != NULL) {112link->header = header;113link->header->listEnter = link;114link->next = MemoryList.next;115link->freed = FALSE;116MemoryList.next = link;117}118119return link;120}121122static int DMem_VerifyGuardArea(const byte_t * area) {123int nbyte;124125for ( nbyte = 0; nbyte < MAX_GUARD_BYTES; nbyte++ ) {126if (area[nbyte] != ByteGuard) {127return FALSE;128}129}130return TRUE;131}132133static void DMem_VerifyHeader(MemoryBlockHeader * header) {134DASSERTMSG( DMem_ClientCheckPtr(header, sizeof(MemoryBlockHeader)), "Invalid header" );135DASSERTMSG( DMem_VerifyGuardArea(header->guard), "Header corruption, possible underwrite" );136DASSERTMSG( header->linenumber > 0 && header->linenumber < MAX_LINENUM, "Header corruption, bad line number" );137DASSERTMSG( header->size <= DMemGlobalState.biggestBlock, "Header corruption, block size is too large");138DASSERTMSG( header->order <= DMemGlobalState.totalAllocs, "Header corruption, block order out of range");139}140141static void DMem_VerifyTail(MemoryBlockTail * tail) {142DASSERTMSG( DMem_ClientCheckPtr(tail, sizeof(MemoryBlockTail)), "Tail corruption, invalid pointer");143DASSERTMSG( DMem_VerifyGuardArea(tail->guard), "Tail corruption, possible overwrite" );144}145146static MemoryBlockHeader * DMem_VerifyBlock(void * memptr) {147MemoryBlockHeader * header;148MemoryBlockTail * tail;149150/* check if the pointer is valid */151DASSERTMSG( DMem_ClientCheckPtr(memptr, 1), "Invalid pointer");152153/* check if the block header is valid */154header = (MemoryBlockHeader *)((byte_t *)memptr - sizeof(MemoryBlockHeader));155DMem_VerifyHeader(header);156/* check that the memory itself is valid */157DASSERTMSG( DMem_ClientCheckPtr(memptr, DMEM_MIN(MAX_CHECK_BYTES,header->size)), "Block memory invalid" );158/* check that the pointer to the alloc list is valid */159DASSERTMSG( DMem_ClientCheckPtr(header->listEnter, sizeof(MemoryListLink)), "Header corruption, alloc list pointer invalid" );160/* check the tail of the block for overruns */161tail = (MemoryBlockTail *) ( (byte_t *)memptr + header->size );162DMem_VerifyTail(tail);163164return header;165}166167static MemoryBlockHeader * DMem_GetHeader(void * memptr) {168MemoryBlockHeader * header = DMem_VerifyBlock(memptr);169return header;170}171172/*173* Should be called before any other DMem_XXX function174*/175void DMem_Initialize() {176DMemMutex = DMutex_Create();177DMutex_Enter(DMemMutex);178DMemGlobalState.pfnAlloc = NULL;179DMemGlobalState.pfnFree = NULL;180DMemGlobalState.pfnCheckPtr = NULL;181DMemGlobalState.biggestBlock = 0;182DMemGlobalState.maxHeap = INT_MAX;183DMemGlobalState.totalHeapUsed = 0;184DMemGlobalState.failNextAlloc = FALSE;185DMemGlobalState.totalAllocs = 0;186DMutex_Exit(DMemMutex);187}188189void DMem_Shutdown() {190DMutex_Destroy(DMemMutex);191}192/*193* Allocates a block of memory, reserving extra space at the start and end of the194* block to store debug info on where the block was allocated, it's size, and195* 'guard' areas to catch overwrite/underwrite bugs196*/197void * DMem_AllocateBlock(size_t size, const char * filename, int linenumber) {198MemoryBlockHeader * header;199MemoryBlockTail * tail;200size_t debugBlockSize;201byte_t * memptr = NULL;202203DMutex_Enter(DMemMutex);204if (DMemGlobalState.failNextAlloc) {205/* force an allocation failure if so ordered */206DMemGlobalState.failNextAlloc = FALSE; /* reset flag */207goto Exit;208}209210/* allocate a block large enough to hold extra debug info */211debugBlockSize = sizeof(MemoryBlockHeader) + size + sizeof(MemoryBlockTail);212header = (MemoryBlockHeader *)DMem_ClientAllocate(debugBlockSize);213if (header == NULL) {214goto Exit;215}216217/* add block to list of allocated memory */218header->listEnter = DMem_TrackBlock(header);219if ( header->listEnter == NULL ) {220DMem_ClientFree(header);221goto Exit;222}223224/* store size of requested block */225header->size = size;226/* update maximum block size */227DMemGlobalState.biggestBlock = DMEM_MAX(header->size, DMemGlobalState.biggestBlock);228/* update used memory total */229DMemGlobalState.totalHeapUsed += header->size;230/* store filename and linenumber where allocation routine was called */231strncpy(header->filename, filename, FILENAME_MAX);232header->linenumber = linenumber;233/* store the order the block was allocated in */234header->order = DMemGlobalState.totalAllocs++;235/* initialize memory to a recognizable 'inited' value */236memptr = (byte_t *)header + sizeof(MemoryBlockHeader);237memset(memptr, ByteInited, size);238/* put guard area before block */239memset(header->guard, ByteGuard, MAX_GUARD_BYTES);240/* put guard area after block */241tail = (MemoryBlockTail *)(memptr + size);242memset(tail->guard, ByteGuard, MAX_GUARD_BYTES);243244Exit:245DMutex_Exit(DMemMutex);246return memptr;247}248249/*250* Frees block of memory allocated with DMem_AllocateBlock251*/252void DMem_FreeBlock(void * memptr) {253MemoryBlockHeader * header;254255DMutex_Enter(DMemMutex);256if ( memptr == NULL) {257goto Exit;258}259260/* get the debug block header preceding the allocated memory */261header = DMem_GetHeader(memptr);262/* fill memory with recognizable 'freed' value */263memset(memptr, ByteFreed, header->size);264/* mark block as freed */265header->listEnter->freed = TRUE;266/* update used memory total */267DMemGlobalState.totalHeapUsed -= header->size;268Exit:269DMutex_Exit(DMemMutex);270}271272static void DMem_DumpHeader(MemoryBlockHeader * header) {273char report[FILENAME_MAX+MAX_DECIMAL_DIGITS*3+1];274static const char * reportFormat =275"file: %s, line %d\n"276"size: %d bytes\n"277"order: %d\n"278"-------";279280DMem_VerifyHeader(header);281sprintf(report, reportFormat, header->filename, header->linenumber, header->size, header->order);282DTRACE_PRINTLN(report);283}284285/*286* Call this function at shutdown time to report any leaked blocks287*/288void DMem_ReportLeaks() {289MemoryListLink * link;290291DMutex_Enter(DMemMutex);292293/* Force memory leaks to be output regardless of trace settings */294DTrace_EnableFile(__FILE__, TRUE);295DTRACE_PRINTLN("--------------------------");296DTRACE_PRINTLN("Debug Memory Manager Leaks");297DTRACE_PRINTLN("--------------------------");298299/* walk through allocated list and dump any blocks not marked as freed */300link = MemoryList.next;301while (link != NULL) {302if ( !link->freed ) {303DMem_DumpHeader(link->header);304}305link = link->next;306}307308DMutex_Exit(DMemMutex);309}310311void DMem_SetAllocCallback( DMEM_ALLOCFN pfn ) {312DMutex_Enter(DMemMutex);313DMemGlobalState.pfnAlloc = pfn;314DMutex_Exit(DMemMutex);315}316317void DMem_SetFreeCallback( DMEM_FREEFN pfn ) {318DMutex_Enter(DMemMutex);319DMemGlobalState.pfnFree = pfn;320DMutex_Exit(DMemMutex);321}322323void DMem_SetCheckPtrCallback( DMEM_CHECKPTRFN pfn ) {324DMutex_Enter(DMemMutex);325DMemGlobalState.pfnCheckPtr = pfn;326DMutex_Exit(DMemMutex);327}328329void DMem_DisableMutex() {330DMemMutex = NULL;331}332333#endif /* defined(DEBUG) */334335/* The following line is only here to prevent compiler warnings336* on release (non-debug) builds337*/338static int dummyVariable = 0;339340341