Files
mars-flaim/ftk/src/ftkmem.cpp

6170 lines
134 KiB
C++

//------------------------------------------------------------------------------
// Desc: This file contains the f_alloc, f_calloc, f_realloc, f_recalloc,
// and f_free routines.
//
// Tabs: 3
//
// Copyright (c) 1991, 1993, 1995-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: $
//------------------------------------------------------------------------------
#include "ftksys.h"
// Cell sizes for buffer allocator
#define CELL_SIZE_0 16
#define CELL_SIZE_1 32
#define CELL_SIZE_2 64
#define CELL_SIZE_3 128
#define CELL_SIZE_4 192
#define CELL_SIZE_5 320
#define CELL_SIZE_6 512
#define CELL_SIZE_7 672
#define CELL_SIZE_8 832
#define CELL_SIZE_9 1088
#define CELL_SIZE_10 1344
#define CELL_SIZE_11 1760
#define CELL_SIZE_12 2176
#define CELL_SIZE_13 2848
#define CELL_SIZE_14 3520
#define CELL_SIZE_15 4608
#define CELL_SIZE_16 5152
#define CELL_SIZE_17 5696
#define CELL_SIZE_18 8164
#define CELL_SIZE_19 13068
#define CELL_SIZE_20 16340
#define CELL_SIZE_21 21796
#define MAX_CELL_SIZE CELL_SIZE_21
#define NUM_BUF_ALLOCATORS 22
#if defined( FLM_RING_ZERO_NLM)
extern rtag_t gv_lAllocRTag;
#define os_malloc(size) \
Alloc( (size), gv_lAllocRTag)
void * nlm_realloc(
void * pMemory,
size_t newSize);
#define os_realloc nlm_realloc
#define os_free Free
#else
#define os_malloc malloc
#define os_realloc realloc
#define os_free free
#endif
#if defined( FLM_UNIX)
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif
#if defined( FLM_UNIX) && !defined( FLM_OSX)
#include <dlfcn.h>
#endif
/************************************************************************
Desc:
*************************************************************************/
typedef struct F_MemHdrTag
{
FLMUINT uiDataSize;
#ifdef FLM_DEBUG
const char * pszFileName;
int iLineNumber;
FLMBOOL bAllocFromNewOp;
FLMUINT uiAllocationId;
FLMUINT uiAllocCnt;
FLMUINT * puiStack;
#endif
#if FLM_ALIGN_SIZE == 8
FLMUINT uiDummy;
#endif
} F_MEM_HDR;
/************************************************************************
Desc:
*************************************************************************/
#define F_GET_ALLOC_PTR( pDataPtr) \
(FLMBYTE *)((FLMBYTE *)(pDataPtr) - sizeof( F_MEM_HDR))
/************************************************************************
Desc:
*************************************************************************/
#define F_GET_DATA_PTR( pAllocPtr) \
(FLMBYTE *)((FLMBYTE *)(pAllocPtr) + sizeof( F_MEM_HDR))
/************************************************************************
Desc:
*************************************************************************/
#define F_GET_MEM_DATA_SIZE( pDataPtr) \
(((F_MEM_HDR *)(F_GET_ALLOC_PTR( pDataPtr)))->uiDataSize)
/************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_WIN) || defined( FLM_UNIX) || defined( FLM_NLM)
#define PTR_IN_MBLK(p,bp,offs) \
(((FLMBYTE *)(p) > (FLMBYTE *)(bp)) && \
((FLMBYTE *)(p) <= (FLMBYTE *)(bp) + (offs)))
#else
#error Platform not supported
#endif
#ifdef FLM_DEBUG
static FLMBOOL gv_bMemTrackingInitialized = FALSE;
static FLMUINT gv_uiInitThreadId = 0;
static F_MUTEX gv_hMemTrackingMutex = F_MUTEX_NULL;
static FLMUINT gv_uiMemTrackingPtrArraySize = 0;
static FLMUINT gv_uiNumMemPtrs = 0;
static void ** gv_ppvMemTrackingPtrs = NULL;
static FLMUINT gv_uiNextMemPtrSlotToUse = 0;
static FLMUINT gv_uiAllocCnt = 0;
static FLMBOOL gv_bTrackLeaks = FALSE;
static FLMBOOL gv_bStackWalk = FALSE;
static FLMBOOL gv_bLogLeaks = FALSE;
#endif
#ifdef FLM_WIN
static HANDLE gv_hMemProcess;
#endif
#define MEM_PTR_INIT_ARRAY_SIZE 512
#define MEM_MAX_STACK_WALK_DEPTH 32
#define F_PICKET_FENCE "FFFFFFFF"
#if defined( FLM_DEBUG)
#define F_PICKET_FENCE_SIZE 8
#else
#define F_PICKET_FENCE_SIZE 0
#endif
#ifdef FLM_DEBUG
FSTATIC FLMUINT * memWalkStack( void);
FSTATIC FLMBOOL initMemTracking( void);
FSTATIC void saveMemTrackingInfo(
F_MEM_HDR * pHdr);
FSTATIC void updateMemTrackingInfo(
F_MEM_HDR * pHdr);
FSTATIC void freeMemTrackingInfo(
FLMBOOL bMutexAlreadyLocked,
FLMUINT uiId,
FLMUINT * puiStack);
#endif
/****************************************************************************
Desc:
****************************************************************************/
class F_SlabManager : public IF_SlabManager
{
public:
F_SlabManager();
virtual ~F_SlabManager();
RCODE FLMAPI setup(
FLMUINT uiPreallocSize);
RCODE FLMAPI allocSlab(
void ** ppSlab);
void FLMAPI freeSlab(
void ** ppSlab);
RCODE FLMAPI resize(
FLMUINT uiNumBytes,
FLMBOOL bPreallocate,
FLMUINT * puiActualSize = NULL);
void FLMAPI incrementTotalBytesAllocated(
FLMUINT uiCount)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
m_uiTotalBytesAllocated += uiCount;
f_mutexUnlock( m_hMutex);
}
void FLMAPI decrementTotalBytesAllocated(
FLMUINT uiCount)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
f_assert( m_uiTotalBytesAllocated >= uiCount);
m_uiTotalBytesAllocated -= uiCount;
f_mutexUnlock( m_hMutex);
}
FLMUINT FLMAPI getSlabSize( void)
{
return( m_uiSlabSize);
}
FLMUINT FLMAPI getTotalSlabs( void)
{
return( m_uiTotalSlabs);
}
FLMUINT FLMAPI totalBytesAllocated( void)
{
return( m_uiTotalBytesAllocated);
}
FLMUINT FLMAPI getTotalSlabBytesAllocated( void)
{
FLMUINT uiTotalSlabBytes;
f_mutexLock( m_hMutex);
uiTotalSlabBytes = m_uiSlabSize * m_uiTotalSlabs;
f_mutexUnlock( m_hMutex);
return( uiTotalSlabBytes);
}
FLMUINT FLMAPI availSlabs( void)
{
return( m_uiAvailSlabs);
}
private:
void freeAllSlabs( void);
void * allocSlabFromSystem( void);
void releaseSlabToSystem(
void * pSlab);
RCODE sortSlabList( void);
typedef struct
{
void * pPrev;
void * pNext;
} SLABHEADER;
static FLMINT FLMAPI slabAddrCompareFunc(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2);
static void FLMAPI slabAddrSwapFunc(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2);
F_MUTEX m_hMutex;
FLMUINT m_uiTotalBytesAllocated;
void * m_pFirstInSlabList;
void * m_pLastInSlabList;
FLMUINT m_uiSlabSize;
FLMUINT m_uiTotalSlabs;
FLMUINT m_uiAvailSlabs;
FLMUINT m_uiInUseSlabs;
FLMUINT m_uiPreallocSlabs;
friend class F_FixedAlloc;
};
/****************************************************************************
Desc:
****************************************************************************/
typedef struct SLAB
{
void * pvAllocator;
SLAB * pNext;
SLAB * pPrev;
SLAB * pNextSlabWithAvailCells;
SLAB * pPrevSlabWithAvailCells;
FLMBYTE * pLocalAvailCellListHead;
FLMUINT16 ui16NextNeverUsedCell;
FLMUINT16 ui16AvailCellCount;
FLMUINT16 ui16AllocatedCells;
} SLAB;
/****************************************************************************
Desc:
****************************************************************************/
typedef struct CELLHEADER
{
SLAB * pContainingSlab;
#ifdef FLM_DEBUG
FLMUINT * puiStack;
#endif
} CELLHEADER;
/****************************************************************************
Desc:
****************************************************************************/
typedef struct CELLHEADER2
{
CELLHEADER cellHeader;
IF_Relocator * pRelocator;
} CELLHEADER2;
/****************************************************************************
Desc:
****************************************************************************/
typedef struct CELLAVAILNEXT
{
FLMBYTE * pNextInList;
#ifdef FLM_DEBUG
FLMBYTE szDebugPattern[ 8];
#endif
} CELLAVAILNEXT;
/****************************************************************************
Desc: Class to provide an efficient means of providing many allocations
of a fixed size.
****************************************************************************/
class F_FixedAlloc : public IF_FixedAlloc
{
public:
F_FixedAlloc();
virtual ~F_FixedAlloc();
RCODE FLMAPI setup(
FLMBOOL bMultiThreaded,
IF_SlabManager * pSlabManager,
IF_Relocator * pDefaultRelocator,
FLMUINT uiCellSize,
FLM_SLAB_USAGE * pUsageStats,
FLMUINT * puiTotalBytesAllocated);
void * FLMAPI allocCell(
IF_Relocator * pRelocator,
void * pvInitialData = NULL,
FLMUINT uiDataSize = 0);
void * FLMAPI allocCell(
IF_Relocator * pRelocator,
F_ALLOC_INIT_FUNC fnAllocInit);
void FLMAPI freeCell(
void * ptr);
void FLMAPI freeUnused( void);
void FLMAPI freeAll( void);
FLMUINT FLMAPI getCellSize( void)
{
return( m_uiCellSize);
}
void FLMAPI defragmentMemory( void);
private:
void * getCell(
IF_Relocator * pRelocator);
SLAB * getAnotherSlab( void);
static FLMUINT getAllocAlignedSize(
FLMUINT uiAskedForSize)
{
return( (uiAskedForSize + FLM_ALLOC_ALIGN) & (~FLM_ALLOC_ALIGN));
}
void freeSlab(
SLAB * pSlab);
void freeCell(
void * pCell,
FLMBOOL bFreeIfEmpty,
FLMBOOL * pbFreedSlab);
#ifdef FLM_DEBUG
void testForLeaks( void);
#endif
static FLMINT FLMAPI slabAddrCompareFunc(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2)
{
SLAB * pSlab1 = (((SLAB **)pvBuffer)[ uiPos1]);
SLAB * pSlab2 = (((SLAB **)pvBuffer)[ uiPos2]);
f_assert( pSlab1 != pSlab2);
if( pSlab1 < pSlab2)
{
return( -1);
}
return( 1);
}
static void FLMAPI slabAddrSwapFunc(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2)
{
SLAB ** ppSlab1 = &(((SLAB **)pvBuffer)[ uiPos1]);
SLAB ** ppSlab2 = &(((SLAB **)pvBuffer)[ uiPos2]);
SLAB * pTmp;
pTmp = *ppSlab1;
*ppSlab1 = *ppSlab2;
*ppSlab2 = pTmp;
}
IF_SlabManager * m_pSlabManager;
SLAB * m_pFirstSlab;
SLAB * m_pLastSlab;
SLAB * m_pFirstSlabWithAvailCells;
SLAB * m_pLastSlabWithAvailCells;
IF_Relocator * m_pDefaultRelocator;
FLMBOOL m_bAvailListSorted;
FLMUINT m_uiSlabsWithAvailCells;
FLMUINT m_uiSlabHeaderSize;
FLMUINT m_uiCellHeaderSize;
FLMUINT m_uiCellSize;
FLMUINT m_uiSizeOfCellAndHeader;
FLMUINT m_uiTotalFreeCells;
FLMUINT m_uiCellsPerSlab;
FLMUINT m_uiSlabSize;
FLM_SLAB_USAGE * m_pUsageStats;
FLMUINT * m_puiTotalBytesAllocated;
F_MUTEX m_hMutex;
friend class F_BufferAlloc;
friend class F_MultiAlloc;
};
/****************************************************************************
Desc:
****************************************************************************/
class F_BufferAlloc : public IF_BufferAlloc
{
public:
F_BufferAlloc()
{
f_memset( m_ppAllocators, 0, sizeof( m_ppAllocators));
m_pSlabManager = NULL;
m_hMutex = F_MUTEX_NULL;
}
virtual ~F_BufferAlloc();
RCODE FLMAPI setup(
FLMBOOL bMultiThreaded,
IF_SlabManager * pSlabManager,
IF_Relocator * pDefaultRelocator,
FLM_SLAB_USAGE * pUsageStats,
FLMUINT * puiTotalBytesAllocated);
RCODE FLMAPI allocBuf(
IF_Relocator * pRelocator,
FLMUINT uiSize,
void * pvInitialData,
FLMUINT uiDataSize,
FLMBYTE ** ppucBuffer,
FLMBOOL * pbAllocatedOnHeap = NULL);
RCODE FLMAPI allocBuf(
IF_Relocator * pRelocator,
FLMUINT uiSize,
F_ALLOC_INIT_FUNC fnAllocInit,
FLMBYTE ** ppucBuffer,
FLMBOOL * pbAllocatedOnHeap = NULL);
RCODE FLMAPI reallocBuf(
IF_Relocator * pRelocator,
FLMUINT uiOldSize,
FLMUINT uiNewSize,
void * pvInitialData,
FLMUINT uiDataSize,
FLMBYTE ** ppucBuffer,
FLMBOOL * pbAllocatedOnHeap = NULL);
void FLMAPI freeBuf(
FLMUINT uiSize,
FLMBYTE ** ppucBuffer);
FLMUINT FLMAPI getTrueSize(
FLMUINT uiSize,
FLMBYTE * pucBuffer);
FLMUINT FLMAPI getMaxCellSize( void)
{
return( MAX_CELL_SIZE);
}
void FLMAPI defragmentMemory( void);
private:
IF_FixedAlloc * getAllocator(
FLMUINT uiSize);
IF_SlabManager * m_pSlabManager;
IF_FixedAlloc * m_ppAllocators[ NUM_BUF_ALLOCATORS];
F_MUTEX m_hMutex;
};
/****************************************************************************
Desc:
****************************************************************************/
class F_MultiAlloc : public IF_MultiAlloc
{
public:
F_MultiAlloc()
{
m_pSlabManager = NULL;
m_puiCellSizes = NULL;
m_ppAllocators = NULL;
m_hMutex = F_MUTEX_NULL;
}
~F_MultiAlloc()
{
cleanup();
}
RCODE FLMAPI setup(
FLMBOOL bMultiThreaded,
IF_SlabManager * pSlabManager,
IF_Relocator * pDefaultRelocator,
FLMUINT * puiCellSizes,
FLM_SLAB_USAGE * pUsageStats,
FLMUINT * puiTotalBytesAllocated);
RCODE FLMAPI allocBuf(
IF_Relocator * pRelocator,
FLMUINT uiSize,
FLMBYTE ** ppucBuffer);
RCODE FLMAPI allocBuf(
IF_Relocator * pRelocator,
FLMUINT uiSize,
F_ALLOC_INIT_FUNC fnAllocInit,
FLMBYTE ** ppucBuffer);
RCODE FLMAPI reallocBuf(
IF_Relocator * pRelocator,
FLMUINT uiNewSize,
FLMBYTE ** ppucBuffer);
void FLMAPI freeBuf(
FLMBYTE ** ppucBuffer);
void FLMAPI defragmentMemory( void);
FLMUINT FLMAPI getTrueSize(
FLMBYTE * pucBuffer);
private:
IF_FixedAlloc * getAllocator(
FLMUINT uiSize);
IF_FixedAlloc * getAllocator(
FLMBYTE * pucBuffer);
void cleanup( void);
IF_SlabManager * m_pSlabManager;
FLMUINT * m_puiCellSizes;
IF_FixedAlloc ** m_ppAllocators;
F_MUTEX m_hMutex;
};
class F_ObjRefTracker;
/****************************************************************************
Desc:
****************************************************************************/
typedef RCODE (* ADDR_FMT_HOOK)( // Address formatter / translator
F_ObjRefTracker * pRefTracker, // Reference tracker object
void * pAddress, // Pointer to the address
FLMBYTE * pucBuffer, // Buffer for formatted address
FLMUINT uiSize, // Size of buffer
void * pvUserData); // User-supplied callback data
/****************************************************************************
Desc:
****************************************************************************/
class F_ObjRefTracker : public F_Object
{
public:
F_ObjRefTracker( void);
virtual ~F_ObjRefTracker( void);
RCODE setup(
const char * pszObjName,
FLMBOOL bLogToFile = FALSE);
void trackRef(
void * pReferenceID,
void * pSubrefID = NULL);
void untrackRef(
void * referenceID,
void * subrefID = NULL);
void checkForUnreleasedRefs(
FLMUINT * puiCount = NULL);
void setAddressFormatter(
ADDR_FMT_HOOK pFunc,
void * pvUserData);
void setModuleHandle(
void * pModHandle)
{
m_pModHandle = pModHandle;
}
private:
F_MUTEX m_hRefListMutex;
F_ListManager * m_pListManager;
F_ListNode m_lnode;
FLMUINT m_lOptions;
FLMUINT m_lCallStackDepth;
#define FORTRACK_MAX_OBJ_NAME_LEN 63
char m_pszObjName[ FORTRACK_MAX_OBJ_NAME_LEN + 1];
IF_FileSystem * m_pFileSystem;
ADDR_FMT_HOOK m_pAddrFmtHook;
void * m_pUserData;
void * m_pModHandle;
char m_pLogPath[ F_PATH_MAX_SIZE];
void formatAddress(
char * pucBuffer,
FLMUINT uiSize,
void * pAddress);
static void getCallStack(
void * stack[],
FLMUINT uiCount,
FLMUINT uiSkip);
void logError(
const char * pucMessage);
RCODE logMessage(
const char * pucMessage,
IF_FileHdl * pFileHdl,
FLMUINT64 * pui64FileCursor);
};
/****************************************************************************
Desc:
****************************************************************************/
class F_TrackingRecord : public F_ListItem
{
public:
F_TrackingRecord( void * pReferenceID, void * pSubrefID)
{
m_pReferenceID = pReferenceID;
m_pSubrefID = pSubrefID;
m_uiThreadID = f_threadId();
f_memset( m_stack, 0, sizeof(m_stack));
}
virtual ~F_TrackingRecord()
{
}
void * getReferenceID()
{
return m_pReferenceID;
}
void * getSubrefID()
{
return m_pSubrefID;
}
FLMUINT getThreadID()
{
return m_uiThreadID;
}
void * getStack()
{
return m_stack;
}
private:
void * m_pReferenceID;
void * m_pSubrefID;
FLMUINT m_uiThreadID;
#define CTRC_STACK_SIZE 20
void * m_stack[ CTRC_STACK_SIZE + 1];
};
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI FlmAllocSlabManager(
IF_SlabManager ** ppSlabManager)
{
if( (*ppSlabManager = f_new F_SlabManager) == NULL)
{
return( RC_SET( NE_FLM_MEM));
}
return( NE_FLM_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI FlmAllocFixedAllocator(
IF_FixedAlloc ** ppFixedAllocator)
{
if( (*ppFixedAllocator = f_new F_FixedAlloc) == NULL)
{
return( RC_SET( NE_FLM_MEM));
}
return( NE_FLM_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI FlmAllocBufferAllocator(
IF_BufferAlloc ** ppBufferAllocator)
{
if( (*ppBufferAllocator = f_new F_BufferAlloc) == NULL)
{
return( RC_SET( NE_FLM_MEM));
}
return( NE_FLM_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI FlmAllocMultiAllocator(
IF_MultiAlloc ** ppMultiAllocator)
{
if( (*ppMultiAllocator = f_new F_MultiAlloc) == NULL)
{
return( RC_SET( NE_FLM_MEM));
}
return( NE_FLM_OK);
}
/************************************************************************
Desc:
*************************************************************************/
FLMUINT f_msize(
void * pvPtr)
{
#if defined( FLM_UNIX)
return( pvPtr ? F_GET_MEM_DATA_SIZE( (pvPtr)) : 0);
#elif defined( FLM_LIBC_NLM)
return( pvPtr ? msize( (F_GET_ALLOC_PTR( (pvPtr)))) : 0);
#elif defined( FLM_RING_ZERO_NLM)
return( pvPtr ? (unsigned)SizeOfAllocBlock(
(F_GET_ALLOC_PTR( (pvPtr)))) : 0);
#else
return( pvPtr ? _msize( (F_GET_ALLOC_PTR( (pvPtr)))) : 0);
#endif
}
/************************************************************************
Desc: Returns the current value of EBP--the value of the caller's stack
frame pointer.
*************************************************************************/
#ifdef FLM_NLM
void * memGetEBP(void);
#ifdef FLM_MWERKS_NLM
void * memGetEBP(void)
{
__asm
{
mov eax,[ebp]
}
}
#else
#pragma aux memGetEBP = "mov eax,ebp";
#endif
#endif
/************************************************************************
Desc: Returns the value at SS:[POS+OFFSET].
*************************************************************************/
#ifdef FLM_NLM
void * memValueAtStackOffset(void *pos, int offset);
#ifdef FLM_MWERKS_NLM
void * memValueAtStackOffset(void *, int)
{
__asm
{
mov eax,[ebp+0x8]
mov ebx,[ebp+0xC]
mov eax,ss:[eax+ebx]
}
}
#else
#pragma aux memValueAtStackOffset = "mov eax,ss:[eax+ebx]" parm [eax] [ebx];
#endif
#endif
#ifdef FLM_DEBUG
/************************************************************************
Desc:
*************************************************************************/
#ifdef FLM_NLM
FSTATIC FLMUINT * memWalkStack( void)
{
FLMUINT uiLoop;
FLMUINT uiRtnAddr;
FLMUINT uiEbp = (FLMUINT) memGetEBP();
FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1];
FLMUINT * puiAddresses;
uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0);
uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 4);
for (uiLoop = 0; uiLoop < MEM_MAX_STACK_WALK_DEPTH; uiLoop++)
{
FLMUINT uiOldEbp;
uiAddresses [uiLoop] = uiRtnAddr;
if (!uiEbp)
{
break;
}
uiOldEbp = uiEbp;
uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0);
if (!uiEbp || uiEbp <= uiOldEbp || uiEbp > uiOldEbp + 5000)
{
break;
}
uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *) uiEbp, 4);
}
uiAddresses [uiLoop] = 0;
if ((puiAddresses = (FLMUINT *)os_malloc(
sizeof( FLMUINT) * (uiLoop+1))) != NULL)
{
f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1));
}
return( puiAddresses);
}
#endif
/********************************************************************
Desc: Walk the call stack.
*********************************************************************/
#ifdef FLM_WIN
FSTATIC FLMUINT * memWalkStack( void)
{
STACKFRAME64 stackFrame;
CONTEXT context;
DWORD machineType;
FLMUINT uiLoop;
FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1];
FLMUINT * puiAddresses;
HANDLE hThread;
HANDLE hProcess;
FLMUINT uiAddrCount;
f_memset( &stackFrame, 0, sizeof( stackFrame));
f_memset( &context, 0, sizeof( context));
#ifdef FLM_64BIT
machineType = IMAGE_FILE_MACHINE_IA64;
#else
machineType = IMAGE_FILE_MACHINE_I386;
#endif
// While you can continue walking the stack...
unsigned vEBP, vEIP;
__asm mov vEBP, ebp
__asm call near nextinstr
nextinstr:
__asm pop vEIP;
context.Ebp = vEBP;
context.Eip = vEIP;
stackFrame.AddrPC.Offset = vEIP;
stackFrame.AddrFrame.Offset = vEBP;
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Mode = AddrModeFlat;
// Must lock the mutex because StackWalk is not thread safe.
f_mutexLock( gv_hMemTrackingMutex);
hProcess = OpenProcess( PROCESS_VM_READ, FALSE, GetCurrentProcessId());
hThread = OpenThread( THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME,
FALSE, GetCurrentThreadId());
// We have already processed the address inside memWalkStack
uiAddrCount = 1;
uiLoop = 0;
for (;;)
{
if (!StackWalk64( machineType, hProcess, hThread, &stackFrame,
&context, NULL,
SymFunctionTableAccess64, SymGetModuleBase64, NULL))
{
break;
}
// Skip the first two addresses. These represent the following:
// 1) memWalkStack
// 2) saveMemTrackingInfo or updateMemTrackingInfo
// We don't need to see them in the stack trace.
uiAddrCount++;
if (uiAddrCount > 2)
{
uiAddresses [uiLoop] = (FLMUINT)stackFrame.AddrReturn.Offset;
uiLoop++;
if (uiLoop == MEM_MAX_STACK_WALK_DEPTH)
{
break;
}
}
}
f_mutexUnlock( gv_hMemTrackingMutex);
uiAddresses [uiLoop] = 0;
if ((puiAddresses = (FLMUINT *)os_malloc(
sizeof( FLMUINT) * (uiLoop+1))) != NULL)
{
f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1));
}
return( puiAddresses);
}
#endif
/********************************************************************
Desc:
*********************************************************************/
#ifdef FLM_UNIX
FSTATIC FLMUINT * memWalkStack( void)
{
return( NULL);
}
#endif
#endif
/********************************************************************
Desc: Initialize memory tracking
*********************************************************************/
#ifdef FLM_DEBUG
FSTATIC FLMBOOL initMemTracking( void)
{
RCODE rc;
F_MUTEX memMutex;
if (!gv_bMemTrackingInitialized && !gv_uiInitThreadId)
{
gv_uiInitThreadId = f_threadId();
rc = f_mutexCreate( &memMutex);
f_sleep( 50);
// Only set to initialized if we were the last thread
// to set gv_uiInitThreadId
if (f_threadId() == gv_uiInitThreadId)
{
if (RC_OK( rc))
{
gv_hMemTrackingMutex = memMutex;
}
else
{
gv_hMemTrackingMutex = F_MUTEX_NULL;
}
#ifdef FLM_WIN
SymSetOptions( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
gv_hMemProcess = GetCurrentProcess();
SymInitialize( gv_hMemProcess, NULL, TRUE);
gv_bTrackLeaks = TRUE;
gv_bStackWalk = FALSE;
#endif
gv_bMemTrackingInitialized = TRUE;
}
else
{
if (RC_OK( rc))
{
f_mutexDestroy( &memMutex);
}
}
}
// Go into a loop until we see initialized flag set to TRUE
// Could be another thread that is doing it.
while (!gv_bMemTrackingInitialized)
{
f_sleep( 10);
}
return( (gv_hMemTrackingMutex != F_MUTEX_NULL) ? TRUE : FALSE);
}
#endif
/********************************************************************
Desc: Save memory tracking information - called on alloc or realloc.
*********************************************************************/
#ifdef FLM_DEBUG
FSTATIC void saveMemTrackingInfo(
F_MEM_HDR * pHdr)
{
FLMUINT uiNewCnt;
FLMUINT uiId;
void ** pNew;
if (gv_bTrackLeaks && initMemTracking())
{
f_mutexLock( gv_hMemTrackingMutex);
// See if there is enough room in the array
if (gv_uiNumMemPtrs == gv_uiMemTrackingPtrArraySize)
{
// If array is not initialized, use initial count. Otherwise
// double the size.
uiNewCnt = (FLMUINT)((!gv_uiMemTrackingPtrArraySize)
? MEM_PTR_INIT_ARRAY_SIZE
: gv_uiMemTrackingPtrArraySize * 2);
if ((pNew = (void **)os_malloc( sizeof( void *) * uiNewCnt)) != NULL)
{
// Copy the pointers from the old array, if any,
// into the newly allocated array.
if (gv_uiMemTrackingPtrArraySize)
{
f_memcpy( pNew, gv_ppvMemTrackingPtrs,
sizeof( void *) * gv_uiMemTrackingPtrArraySize);
os_free( gv_ppvMemTrackingPtrs);
gv_ppvMemTrackingPtrs = NULL;
}
f_memset( &pNew [gv_uiMemTrackingPtrArraySize], 0,
sizeof( void *) * (uiNewCnt - gv_uiMemTrackingPtrArraySize));
gv_ppvMemTrackingPtrs = pNew;
gv_uiMemTrackingPtrArraySize = uiNewCnt;
}
}
// If we are still full, we were not able to reallocate memory, so we
// do nothing.
if (gv_uiNumMemPtrs == gv_uiMemTrackingPtrArraySize)
{
pHdr->uiAllocationId = 0;
}
else
{
// Find an empty slot - there has to be one!
uiId = gv_uiNextMemPtrSlotToUse;
while (gv_ppvMemTrackingPtrs [uiId])
{
if (++uiId == gv_uiMemTrackingPtrArraySize)
{
uiId = 0;
}
}
// Allocation ID in the header is offset by one to avoid
// using a value of zero.
pHdr->uiAllocationId = uiId + 1;
gv_ppvMemTrackingPtrs [uiId] = pHdr;
gv_uiNumMemPtrs++;
if ((gv_uiNextMemPtrSlotToUse = uiId + 1) ==
gv_uiMemTrackingPtrArraySize)
{
gv_uiNextMemPtrSlotToUse = 0;
}
}
pHdr->uiAllocCnt = ++gv_uiAllocCnt;
f_mutexUnlock( gv_hMemTrackingMutex);
}
else
{
pHdr->uiAllocationId = 0;
pHdr->uiAllocCnt = 0;
}
// Follow the stack.
if (gv_bTrackLeaks && gv_bStackWalk)
{
pHdr->puiStack = memWalkStack();
}
else
{
pHdr->puiStack = NULL;
}
}
#endif
/********************************************************************
Desc: Update memory tracking information - called after realloc
*********************************************************************/
#ifdef FLM_DEBUG
FSTATIC void updateMemTrackingInfo(
F_MEM_HDR * pHdr)
{
if (pHdr->puiStack)
{
os_free( pHdr->puiStack);
pHdr->puiStack = NULL;
}
if (gv_bTrackLeaks && gv_bStackWalk)
{
pHdr->puiStack = memWalkStack();
}
}
#endif
/********************************************************************
Desc: Free memory tracking information - called on free.
*********************************************************************/
#ifdef FLM_DEBUG
FSTATIC void freeMemTrackingInfo(
FLMBOOL bMutexAlreadyLocked,
FLMUINT uiId,
FLMUINT * puiStack)
{
if( uiId)
{
// NOTE: If uiId is non-zero, it means we had to have
// successfully initialized, so we are guaranteed to
// have a mutex.
if( !bMutexAlreadyLocked)
{
f_mutexLock( gv_hMemTrackingMutex);
}
// Allocation ID in the header is offset by one so that it
// is never zero - a value of zero means that the allocation
// does not have a slot for tracking it in the array.
if( gv_ppvMemTrackingPtrs)
{
gv_ppvMemTrackingPtrs[ uiId - 1] = NULL;
f_assert( gv_uiNumMemPtrs);
gv_uiNumMemPtrs--;
}
if ( !bMutexAlreadyLocked)
{
f_mutexUnlock( gv_hMemTrackingMutex);
}
}
// Free the stack information, if any.
if( puiStack)
{
os_free( puiStack);
}
}
#endif
/********************************************************************
Desc: Log memory leaks.
*********************************************************************/
#ifdef FLM_DEBUG
void logMemLeak(
F_MEM_HDR * pHdr)
{
char szTmpBuffer [1024];
FLMUINT uiMsgBufSize;
char * pszMessageBuffer;
char * pszTmp;
IF_FileHdl * pFileHdl = NULL;
FLMBOOL bSaveTrackLeaks = gv_bTrackLeaks;
IF_FileSystem * pFileSystem = f_getFileSysPtr();
gv_bTrackLeaks = FALSE;
// Need a big buffer to show an entire stack.
uiMsgBufSize = 20480;
if ((pszMessageBuffer = (char *)os_malloc( uiMsgBufSize)) == NULL)
{
pszMessageBuffer = &szTmpBuffer [0];
uiMsgBufSize = sizeof( szTmpBuffer);
}
pszTmp = pszMessageBuffer;
// Format message to be logged.
f_strcpy( pszTmp, "Abort=Debug, Retry=Continue, Ignore=Don't Show\r\n");
while (*pszTmp)
{
pszTmp++;
}
#if defined( FLM_WIN) && defined( FLM_64BIT)
f_sprintf( pszTmp, "Unfreed Pointer: 0x%016I64x\r\n", (FLMUINT)(&pHdr [1]));
#else
f_sprintf( pszTmp, "Unfreed Pointer: 0x%08x\r\n",
(unsigned)((FLMUINT)(&pHdr [1])));
#endif
while( *pszTmp)
{
pszTmp++;
}
if( pHdr->pszFileName)
{
f_sprintf( pszTmp, "Source: %s, Line#: %u\r\n", pHdr->pszFileName,
(unsigned)pHdr->iLineNumber);
while (*pszTmp)
{
pszTmp++;
}
}
if( pHdr->uiAllocCnt)
{
f_sprintf( pszTmp, "Malloc #: %u\r\n", (unsigned)pHdr->uiAllocCnt);
while (*pszTmp)
{
pszTmp++;
}
}
f_sprintf( (char *)pszTmp, "Size: %u bytes\r\n", (unsigned)pHdr->uiDataSize);
while( *pszTmp)
{
pszTmp++;
}
if( pHdr->puiStack)
{
FLMUINT * puiStack = pHdr->puiStack;
FLMUINT uiLen = pszTmp - pszMessageBuffer;
char szFuncName [200];
char * pszFuncName;
#ifdef FLM_WIN
IMAGEHLP_SYMBOL * pImgHlpSymbol;
pImgHlpSymbol = (IMAGEHLP_SYMBOL *)os_malloc(
sizeof( IMAGEHLP_SYMBOL) + 100);
#endif
while (*puiStack)
{
szFuncName [0] = 0;
#ifdef FLM_WIN
if (pImgHlpSymbol)
{
#ifdef FLM_64BIT
DWORD64 udDisplacement;
#else
DWORD udDisplacement;
#endif
pImgHlpSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
pImgHlpSymbol->Address = *puiStack;
pImgHlpSymbol->MaxNameLength = 100;
if (SymGetSymFromAddr( gv_hMemProcess, *puiStack,
&udDisplacement, pImgHlpSymbol))
{
f_sprintf( szFuncName, "\t%s + %X\r\n",
(char *)(&pImgHlpSymbol->Name [0]),
udDisplacement);
}
}
#else
#ifdef HAVE_DLADDR
{
Dl_info dlip;
if (dladdr( (void *)(*puiStack), &dlip) != 0 && dlip.dli_sname)
{
const char * pszFileName;
if (dlip.dli_saddr != (void *)(*puiStack))
{
pszFileName = strrchr(dlip.dli_fname, '/');
if (!pszFileName)
{
pszFileName = dlip.dli_fname;
}
else
{
pszFileName++; // skip over slash
}
f_sprintf( szFuncName, "\t0x%08x (%s)\r\n",
(unsigned)(*puiStack), pszFileName);
}
else
{
f_sprintf( szFuncName, "\t%s\r\n", dlip.dli_sname);
}
}
}
#endif
#endif
// If szFuncName [0] is zero, we didn't find a name, so we
// just output the address in HEX.
if (!szFuncName [0])
{
f_sprintf( szFuncName, "\t0x%08X\r\n", (unsigned)*puiStack );
}
// Output whatever portion of the name will fit into the
// message buffer.
pszFuncName = &szFuncName [0];
while (*pszFuncName && uiLen < uiMsgBufSize - 1)
{
*pszTmp++ = *pszFuncName++;
uiLen++;
}
// Process next address in the stack.
puiStack++;
}
*pszTmp = 0;
#ifdef FLM_WIN
if (pImgHlpSymbol)
{
os_free( pImgHlpSymbol);
}
#endif
}
#ifdef FLM_WIN
FLMINT iRet;
iRet = MessageBox( NULL, (LPCTSTR)pszMessageBuffer, "WIN32 Memory Testing",
MB_ABORTRETRYIGNORE | MB_ICONINFORMATION | MB_TASKMODAL
| MB_SETFOREGROUND | MB_DEFBUTTON2);
if (iRet == IDIGNORE)
{
gv_bLogLeaks = TRUE;
}
else if (iRet == IDABORT)
{
f_assert( 0);
}
#else
gv_bLogLeaks = TRUE;
#endif
if (gv_bLogLeaks && pFileSystem)
{
RCODE rc;
FLMUINT uiDummy;
#ifdef FLM_NLM
const char * pszErrPath = "sys:\\memtest.ert";
#else
const char * pszErrPath = "memtest.ert";
#endif
if (RC_BAD( rc = pFileSystem->openFile( pszErrPath,
FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pFileHdl)))
{
if (rc == NE_FLM_IO_PATH_NOT_FOUND)
{
rc = pFileSystem->createFile( pszErrPath,
FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pFileHdl);
}
}
else
{
// Position to append to file.
rc = pFileHdl->seek( 0, FLM_IO_SEEK_END, NULL);
}
// If we successfully opened the file, write to it.
if (RC_OK( rc))
{
if (RC_OK( pFileHdl->write( FLM_IO_CURRENT_POS,
(FLMUINT)(pszTmp - pszMessageBuffer),
pszMessageBuffer, &uiDummy)))
{
(void)pFileHdl->flush();
}
pFileHdl->close();
}
}
if (pFileHdl)
{
pFileHdl->Release();
}
if (pszMessageBuffer != &szTmpBuffer [0])
{
os_free( pszMessageBuffer);
}
gv_bTrackLeaks = bSaveTrackLeaks;
}
#endif
/********************************************************************
Desc: Initialize memory - if not already done.
*********************************************************************/
void f_memoryInit( void)
{
#ifdef FLM_DEBUG
(void)initMemTracking();
#endif
#if defined( FLM_UNIX) && defined( RLIMIT_VMEM)
{
struct rlimit rlim;
// Bump the process soft virtual limit up to the hard limit
if( getrlimit( RLIMIT_VMEM, &rlim) == 0)
{
if( rlim.rlim_cur < rlim.rlim_max)
{
rlim.rlim_cur = rlim.rlim_max;
(void)setrlimit( RLIMIT_VMEM, &rlim);
}
}
}
#endif
#if defined( FLM_UNIX) && defined( RLIMIT_DATA)
{
struct rlimit rlim;
// Bump the process soft heap limit up to the hard limit
if( getrlimit( RLIMIT_DATA, &rlim) == 0)
{
if( rlim.rlim_cur < rlim.rlim_max)
{
rlim.rlim_cur = rlim.rlim_max;
(void)setrlimit( RLIMIT_DATA, &rlim);
}
}
}
#elif defined( FLM_OSX)
#error OS X should provide support for RLIMIT_DATA
#endif
}
/********************************************************************
Desc: Clean up memory and check for unfreed memory.
*********************************************************************/
void f_memoryCleanup( void)
{
#ifdef FLM_DEBUG
if (initMemTracking())
{
FLMUINT uiId;
F_MEM_HDR * pHdr;
f_mutexLock( gv_hMemTrackingMutex);
for (uiId = 0; uiId < gv_uiMemTrackingPtrArraySize; uiId++)
{
if ((pHdr = (F_MEM_HDR *)gv_ppvMemTrackingPtrs [uiId]) != NULL)
{
logMemLeak( pHdr);
freeMemTrackingInfo( TRUE, uiId + 1, pHdr->puiStack);
}
}
// Free the memory pointer array.
os_free( gv_ppvMemTrackingPtrs);
gv_ppvMemTrackingPtrs = NULL;
gv_uiMemTrackingPtrArraySize = 0;
gv_uiNumMemPtrs = 0;
f_mutexUnlock( gv_hMemTrackingMutex);
// Free up the mutex.
f_mutexDestroy( &gv_hMemTrackingMutex);
// Reset to unitialized state.
gv_uiInitThreadId = 0;
gv_hMemTrackingMutex = F_MUTEX_NULL;
gv_bMemTrackingInitialized = FALSE;
#ifdef FLM_WIN
SymCleanup( gv_hMemProcess);
#endif
}
#endif
}
/********************************************************************
Desc: Allocate Memory.
*********************************************************************/
RCODE FLMAPI f_allocImp(
FLMUINT uiSize,
void ** ppvPtr,
FLMBOOL bAllocFromNewOp,
const char * pszFileName,
int iLineNumber)
{
RCODE rc = NE_FLM_OK;
F_MEM_HDR * pHdr;
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( bAllocFromNewOp);
F_UNREFERENCED_PARM( pszFileName);
F_UNREFERENCED_PARM( iLineNumber);
#endif
if( (pHdr = (F_MEM_HDR *)os_malloc( uiSize + sizeof( F_MEM_HDR) +
F_PICKET_FENCE_SIZE)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
pHdr->uiDataSize = uiSize;
*ppvPtr = (void *)(&pHdr [1]);
#ifdef FLM_DEBUG
pHdr->bAllocFromNewOp = bAllocFromNewOp;
pHdr->iLineNumber = iLineNumber;
pHdr->pszFileName = pszFileName;
saveMemTrackingInfo( pHdr);
#if F_PICKET_FENCE_SIZE
f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize,
F_PICKET_FENCE, F_PICKET_FENCE_SIZE);
#endif
#endif
Exit:
return( rc);
}
/********************************************************************
Desc: Allocate and initialize memory.
*********************************************************************/
RCODE FLMAPI f_callocImp(
FLMUINT uiSize,
void ** ppvPtr,
const char * pszFileName,
int iLineNumber)
{
RCODE rc = NE_FLM_OK;
F_MEM_HDR * pHdr;
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pszFileName);
F_UNREFERENCED_PARM( iLineNumber);
#endif
if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + sizeof( F_MEM_HDR) +
F_PICKET_FENCE_SIZE)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
pHdr->uiDataSize = uiSize;
*ppvPtr = (void *)(&pHdr [1]);
f_memset( *ppvPtr, 0, uiSize);
#ifdef FLM_DEBUG
pHdr->bAllocFromNewOp = FALSE;
pHdr->iLineNumber = iLineNumber;
pHdr->pszFileName = pszFileName;
saveMemTrackingInfo( pHdr);
#if F_PICKET_FENCE_SIZE
f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize,
F_PICKET_FENCE, F_PICKET_FENCE_SIZE);
#endif
#endif
Exit:
return( rc);
}
/********************************************************************
Desc: Reallocate memory.
*********************************************************************/
RCODE FLMAPI f_reallocImp(
FLMUINT uiSize,
void ** ppvPtr,
const char * pszFileName,
int iLineNumber)
{
RCODE rc = NE_FLM_OK;
F_MEM_HDR * pNewHdr;
#ifdef FLM_DEBUG
F_MEM_HDR * pOldHdr;
FLMUINT uiOldAllocationId;
FLMUINT * puiOldStack;
#endif
if (!(*ppvPtr))
{
rc = f_allocImp( uiSize, ppvPtr, FALSE, pszFileName, iLineNumber);
goto Exit;
}
#ifdef FLM_DEBUG
pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr);
#if F_PICKET_FENCE_SIZE
// Verify the old picket fence
if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize,
F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0)
{
rc = RC_SET_AND_ASSERT( NE_FLM_MEM);
goto Exit;
}
#endif
// Cannot realloc memory that was allocated via a new operator
if (pOldHdr->bAllocFromNewOp)
{
rc = RC_SET_AND_ASSERT( NE_FLM_MEM);
goto Exit;
}
uiOldAllocationId = pOldHdr->uiAllocationId;
puiOldStack = pOldHdr->puiStack;
#endif
if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr),
uiSize + sizeof( F_MEM_HDR) +
F_PICKET_FENCE_SIZE)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
pNewHdr->uiDataSize = uiSize;
*ppvPtr = (void *)(&pNewHdr [1]);
#ifdef FLM_DEBUG
pNewHdr->bAllocFromNewOp = FALSE;
pNewHdr->iLineNumber = iLineNumber;
pNewHdr->pszFileName = pszFileName;
if (pNewHdr != pOldHdr)
{
freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack);
saveMemTrackingInfo( pNewHdr);
}
else
{
updateMemTrackingInfo( pNewHdr);
}
#if F_PICKET_FENCE_SIZE
f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize,
F_PICKET_FENCE, F_PICKET_FENCE_SIZE);
#endif
#endif
Exit:
return( rc);
}
/********************************************************************
Desc: Reallocate memory, and initialize the new part.
*********************************************************************/
RCODE FLMAPI f_recallocImp(
FLMUINT uiSize,
void ** ppvPtr,
const char * pszFileName,
int iLineNumber)
{
RCODE rc = NE_FLM_OK;
F_MEM_HDR * pNewHdr;
FLMUINT uiOldSize;
#ifdef FLM_DEBUG
F_MEM_HDR * pOldHdr;
FLMUINT uiOldAllocationId;
FLMUINT * puiOldStack;
#endif
if (!(*ppvPtr))
{
rc = f_callocImp( uiSize, ppvPtr, pszFileName, iLineNumber);
goto Exit;
}
#ifdef FLM_DEBUG
pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr);
#if F_PICKET_FENCE_SIZE
// Verify the old picket fence
if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize,
F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0)
{
rc = RC_SET_AND_ASSERT( NE_FLM_MEM);
goto Exit;
}
#endif
// Cannot realloc memory that was allocated via a new operator
if (pOldHdr->bAllocFromNewOp)
{
rc = RC_SET_AND_ASSERT( NE_FLM_MEM);
goto Exit;
}
uiOldAllocationId = pOldHdr->uiAllocationId;
puiOldStack = pOldHdr->puiStack;
#endif
uiOldSize = F_GET_MEM_DATA_SIZE( *ppvPtr);
if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr),
uiSize + sizeof( F_MEM_HDR) +
F_PICKET_FENCE_SIZE)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
pNewHdr->uiDataSize = uiSize;
*ppvPtr = (void *)(&pNewHdr [1]);
if (uiOldSize < uiSize)
{
f_memset( ((FLMBYTE *)(*ppvPtr)) + uiOldSize, 0,
uiSize - uiOldSize);
}
#ifdef FLM_DEBUG
pNewHdr->bAllocFromNewOp = FALSE;
pNewHdr->iLineNumber = iLineNumber;
pNewHdr->pszFileName = pszFileName;
if (pNewHdr != pOldHdr)
{
freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack);
saveMemTrackingInfo( pNewHdr);
}
else
{
updateMemTrackingInfo( pNewHdr);
}
#if F_PICKET_FENCE_SIZE
f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize,
F_PICKET_FENCE, F_PICKET_FENCE_SIZE);
#endif
#endif
Exit:
return( rc);
}
/********************************************************************
Desc: Free previously allocated memory.
*********************************************************************/
void FLMAPI f_freeImp(
void ** ppvPtr,
FLMBOOL bFreeFromDeleteOp)
{
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( bFreeFromDeleteOp);
#endif
if (*ppvPtr)
{
#ifdef FLM_DEBUG
F_MEM_HDR * pHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr);
if (pHdr->bAllocFromNewOp && !bFreeFromDeleteOp ||
!pHdr->bAllocFromNewOp && bFreeFromDeleteOp)
{
// Either trying to free memory using f_free when
// allocated from new, or trying to free memory
// using delete when allocated from f_alloc,
// f_calloc, f_realloc, or f_recalloc.
RC_UNEXPECTED_ASSERT( NE_FLM_MEM);
return;
}
#if F_PICKET_FENCE_SIZE
// Check the picket fence
if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pHdr->uiDataSize,
F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0)
{
RC_UNEXPECTED_ASSERT( NE_FLM_MEM);
}
#endif
freeMemTrackingInfo( FALSE, pHdr->uiAllocationId, pHdr->puiStack);
#endif
os_free( F_GET_ALLOC_PTR( *ppvPtr));
*ppvPtr = NULL;
}
}
/********************************************************************
Desc: Reset the stack information for an allocation.
*********************************************************************/
void f_resetStackInfoImp(
void * pvPtr,
const char * pszFileName,
int iLineNumber)
{
#ifdef FLM_DEBUG
if (pvPtr)
{
F_MEM_HDR * pHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( pvPtr);
pHdr->iLineNumber = iLineNumber;
pHdr->pszFileName = pszFileName;
f_mutexLock( gv_hMemTrackingMutex);
pHdr->uiAllocCnt = ++gv_uiAllocCnt;
f_mutexUnlock( gv_hMemTrackingMutex);
updateMemTrackingInfo( pHdr);
}
#else
F_UNREFERENCED_PARM( pvPtr);
F_UNREFERENCED_PARM( pszFileName);
F_UNREFERENCED_PARM( iLineNumber);
#endif
}
/************************************************************************
Desc: Destructor
*************************************************************************/
F_Pool::~F_Pool()
{
poolFree();
}
/************************************************************************
Desc: Initialize a smart pool memory structure. A smart pool is one that
will adjust it's block allocation size based on statistics it
gathers within the POOL_STATS structure. For each pool that user
wants to use smart memory management a global POOL_STATS structure
should be declared. The POOL_STATS structure is used to track the
total bytes allocated and determine what the correct pool block
size should be.
*************************************************************************/
void F_Pool::smartPoolInit(
POOL_STATS * pPoolStats)
{
m_pPoolStats = pPoolStats;
if (m_pPoolStats && m_pPoolStats->uiCount)
{
setInitialSmartPoolBlockSize();
}
else
{
m_uiBlockSize = 2048;
}
}
/****************************************************************************
Desc: Allocates a block of memory from a memory pool.
Note: If the number of bytes is more than the what is left in the
current block then a new block will be allocated and the lbkl element
of the PMS will be updated.
****************************************************************************/
RCODE FLMAPI F_Pool::poolAlloc(
FLMUINT uiSize,
void ** ppvPtr)
{
RCODE rc = NE_FLM_OK;
PoolMemoryBlock * pBlock = m_pLastBlock;
PoolMemoryBlock * pOldLastBlock = pBlock;
FLMBYTE * pucFreePtr;
FLMUINT uiBlockSize;
// Adjust the size to a machine word boundary
// NOTE: ORed and ANDed 0x800.. & 0x7FFF to prevent partial
// stalls on Netware
if (uiSize & (FLM_ALLOC_ALIGN | 0x80000000))
{
uiSize = ((uiSize + FLM_ALLOC_ALIGN) & (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF));
}
// Check if room in block
if (!pBlock || uiSize > pBlock->uiFreeSize)
{
// Check if previous block has space for allocation
if (pBlock &&
pBlock->pPrevBlock &&
uiSize <= pBlock->pPrevBlock->uiFreeSize)
{
pBlock = pBlock->pPrevBlock;
goto Exit;
}
// Not enough memory in block - allocate new block
// Determine the block size:
// 1) start with max of last block size, initial pool size, or alloc size
// 2) if this is an extra block alloc then increase the size by 1/2
// 3) adjust size to include block header
uiBlockSize = (pBlock) ? pBlock->uiBlockSize : m_uiBlockSize;
uiBlockSize = f_max( uiSize, uiBlockSize);
if (pBlock &&
uiBlockSize == pBlock->uiBlockSize &&
uiBlockSize <= 32769)
{
uiBlockSize += uiBlockSize / 2;
}
// Add in extra bytes for block overhead
uiBlockSize += sizeof( PoolMemoryBlock);
if (RC_BAD( rc = f_alloc( uiBlockSize, &pBlock)))
{
goto Exit;
}
// Initialize the block elements
pBlock->uiBlockSize = uiBlockSize;
pBlock->uiFreeOffset = sizeof( PoolMemoryBlock);
pBlock->uiFreeSize = uiBlockSize - sizeof( PoolMemoryBlock);
// Link in newly allocated block
m_pLastBlock = pBlock;
pBlock->pPrevBlock = pOldLastBlock;
}
Exit:
if (RC_OK( rc))
{
pucFreePtr = (FLMBYTE *)pBlock;
pucFreePtr += pBlock->uiFreeOffset;
pBlock->uiFreeOffset += uiSize;
pBlock->uiFreeSize -= uiSize;
m_uiBytesAllocated += uiSize;
*ppvPtr = (void *)pucFreePtr;
}
else
{
*ppvPtr = NULL;
}
return( rc);
}
/****************************************************************************
Desc: Allocates a block of memory from a memory pool.
****************************************************************************/
RCODE FLMAPI F_Pool::poolCalloc(
FLMUINT uiSize,
void ** ppvPtr)
{
RCODE rc;
if (RC_OK( rc = poolAlloc( uiSize, ppvPtr)))
{
f_memset( *ppvPtr, 0, uiSize);
}
return( rc);
}
/****************************************************************************
Desc : Releases all memory allocated to a pool.
Note : All memory allocated to the pool is returned to the operating system.
*****************************************************************************/
void FLMAPI F_Pool::poolFree( void)
{
PoolMemoryBlock * pBlock = m_pLastBlock;
PoolMemoryBlock * pPrevBlock;
// Free all blocks in chain
while (pBlock)
{
pPrevBlock = pBlock->pPrevBlock;
f_free( &pBlock);
pBlock = pPrevBlock;
}
m_pLastBlock = NULL;
// For Smart pools, update pool statictics
if (m_pPoolStats)
{
updateSmartPoolStats();
}
}
/****************************************************************************
Desc: Resets memory blocks allocated to a pool.
Note: Will reset the free space in the first memory block, and if
any extra blocks exist they will be freed (destroyed).
*****************************************************************************/
void FLMAPI F_Pool::poolReset(
void * pvMark,
FLMBOOL bReduceFirstBlock)
{
PoolMemoryBlock * pBlock = m_pLastBlock;
PoolMemoryBlock * pPrevBlock;
if (!pBlock)
{
return;
}
// For Smart Pools update pool statictics
if (m_pPoolStats)
{
updateSmartPoolStats();
}
if (pvMark)
{
freeToMark( pvMark);
return;
}
// Free all blocks except last one in chain -- which is really
// the first block allocated. This will help us keep memory from
// getting fragmented.
while (pBlock->pPrevBlock)
{
pPrevBlock = pBlock->pPrevBlock;
f_free( &pBlock);
pBlock = pPrevBlock;
}
if (pBlock->uiBlockSize - sizeof( PoolMemoryBlock) >
m_uiBlockSize && bReduceFirstBlock)
{
// The first block was not the default size, so free it
f_free( &pBlock);
m_pLastBlock = NULL;
}
else
{
// Reset the allocation pointers in the first block
pBlock->uiFreeOffset = sizeof( PoolMemoryBlock);
pBlock->uiFreeSize = pBlock->uiBlockSize - sizeof( PoolMemoryBlock);
m_pLastBlock = pBlock;
}
// On smart pools adjust the initial block size on pool resets
if (m_pPoolStats)
{
setInitialSmartPoolBlockSize();
}
}
/****************************************************************************
Desc: Frees memory until the pvMark is found.
****************************************************************************/
void F_Pool::freeToMark(
void * pvMark)
{
PoolMemoryBlock * pBlock = m_pLastBlock;
PoolMemoryBlock * pPrevBlock;
// Initialize pool to no blocks
m_pLastBlock = NULL;
while (pBlock)
{
pPrevBlock = pBlock->pPrevBlock;
// Check for mark point
if (PTR_IN_MBLK( pvMark, pBlock, pBlock->uiBlockSize))
{
FLMUINT uiOldFreeOffset = pBlock->uiFreeOffset;
// Reset uiFreeOffset and uiFreeSize variables
pBlock->uiFreeOffset = (FLMUINT)((FLMBYTE *)pvMark -
(FLMBYTE *)pBlock);
pBlock->uiFreeSize = pBlock->uiBlockSize - pBlock->uiFreeOffset;
// For Smart Pools deduct the bytes allocated since pool mark
if (m_pPoolStats)
{
f_assert( uiOldFreeOffset >= pBlock->uiFreeOffset);
m_uiBytesAllocated -= (uiOldFreeOffset - pBlock->uiFreeOffset);
}
break;
}
if (m_pPoolStats)
{
m_uiBytesAllocated -= (pBlock->uiFreeOffset - sizeof( PoolMemoryBlock));
}
f_free( &pBlock);
pBlock = pPrevBlock;
}
if (pBlock)
{
m_pLastBlock = pBlock;
}
}
/****************************************************************************
Desc:
****************************************************************************/
F_SlabManager::F_SlabManager()
{
m_hMutex = F_MUTEX_NULL;
m_uiTotalBytesAllocated = 0;
m_pFirstInSlabList = NULL;
m_pLastInSlabList = NULL;
m_uiTotalSlabs = 0;
m_uiAvailSlabs = 0;
m_uiInUseSlabs = 0;
m_uiPreallocSlabs = 0;
}
/****************************************************************************
Desc:
****************************************************************************/
F_SlabManager::~F_SlabManager()
{
f_assert( !m_uiInUseSlabs);
f_assert( m_uiAvailSlabs == m_uiTotalSlabs);
freeAllSlabs();
f_assert( !m_uiTotalBytesAllocated);
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &m_hMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_SlabManager::setup(
FLMUINT uiPreallocSize)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiSysSlabSize = 0;
FLMUINT uiSlabSize = 64 * 1024;
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
{
goto Exit;
}
// Determine the slab size
#ifdef FLM_WIN
{
SYSTEM_INFO sysInfo;
GetSystemInfo( &sysInfo);
uiSysSlabSize = sysInfo.dwAllocationGranularity;
}
#endif
if( !uiSysSlabSize)
{
uiSysSlabSize = uiSlabSize;
}
// Round the given slab size up to the closest operating
// system slab size so we don't waste any memory.
if( uiSlabSize % uiSysSlabSize)
{
m_uiSlabSize = ((uiSlabSize / uiSysSlabSize) + 1) * uiSysSlabSize;
}
else
{
m_uiSlabSize = uiSlabSize;
}
// Pre-allocate the requested amount of memory from the system
if( uiPreallocSize)
{
if( RC_BAD( rc = resize( uiPreallocSize, TRUE, NULL)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_SlabManager::resize(
FLMUINT uiNumBytes,
FLMBOOL bPreallocate,
FLMUINT * puiActualSize)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiSlabsNeeded;
void * pSlab;
FLMBOOL bMutexLocked = FALSE;
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
if( puiActualSize)
{
*puiActualSize = 0;
}
uiSlabsNeeded = (uiNumBytes / m_uiSlabSize) +
((uiNumBytes % m_uiSlabSize) ? 1 : 0);
if( !uiSlabsNeeded && !m_uiInUseSlabs)
{
freeAllSlabs();
}
else if( m_uiTotalSlabs > uiSlabsNeeded)
{
// Do the best we can to free slabs. We can only get rid of
// slabs that aren't in use.
if( RC_BAD( rc = sortSlabList()))
{
goto Exit;
}
while( m_pLastInSlabList && m_uiTotalSlabs > uiSlabsNeeded)
{
pSlab = m_pLastInSlabList;
if( (m_pLastInSlabList = ((SLABHEADER *)pSlab)->pPrev) != NULL)
{
((SLABHEADER *)m_pLastInSlabList)->pNext = NULL;
}
else
{
m_pFirstInSlabList = NULL;
}
releaseSlabToSystem( pSlab);
f_assert( m_uiTotalSlabs);
f_assert( m_uiInUseSlabs);
f_assert( m_uiTotalBytesAllocated);
m_uiAvailSlabs--;
m_uiTotalSlabs--;
m_uiTotalBytesAllocated -= m_uiSlabSize;
}
}
else if( bPreallocate)
{
// Allocate the required number of slabs
while( m_uiTotalSlabs < uiSlabsNeeded)
{
if( (pSlab = allocSlabFromSystem()) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
// Touch every byte in the slab so that the operating system is
// forced to immediately assign physical memory.
f_memset( pSlab, 0, m_uiSlabSize);
// Link the slab into the avail list
if( m_pFirstInSlabList)
{
((SLABHEADER *)m_pFirstInSlabList)->pPrev = pSlab;
}
((SLABHEADER *)pSlab)->pNext = m_pFirstInSlabList;
m_pFirstInSlabList = pSlab;
if( !m_pLastInSlabList)
{
m_pLastInSlabList = pSlab;
}
m_uiTotalSlabs++;
m_uiAvailSlabs++;
m_uiTotalBytesAllocated += m_uiSlabSize;
}
}
if( puiActualSize)
{
*puiActualSize = m_uiTotalSlabs * m_uiSlabSize;
}
if( bPreallocate)
{
m_uiPreallocSlabs = m_uiTotalSlabs;
}
else
{
m_uiPreallocSlabs = 0;
}
Exit:
if( RC_BAD( rc))
{
freeAllSlabs();
}
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_SlabManager::allocSlab(
void ** ppSlab)
{
RCODE rc = NE_FLM_OK;
FLMBOOL bMutexLocked = FALSE;
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
if( m_pFirstInSlabList)
{
*ppSlab = m_pFirstInSlabList;
if( (m_pFirstInSlabList =
((SLABHEADER *)m_pFirstInSlabList)->pNext) != NULL)
{
((SLABHEADER *)m_pFirstInSlabList)->pPrev = NULL;
}
else
{
m_pLastInSlabList = NULL;
}
((SLABHEADER *)*ppSlab)->pNext = NULL;
f_assert( m_uiAvailSlabs);
m_uiAvailSlabs--;
m_uiInUseSlabs++;
}
else
{
f_assert( !m_uiAvailSlabs);
f_mutexUnlock( m_hMutex);
bMutexLocked = FALSE;
if( (*ppSlab = allocSlabFromSystem()) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
m_uiTotalSlabs++;
m_uiInUseSlabs++;
m_uiTotalBytesAllocated += m_uiSlabSize;
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI F_SlabManager::freeSlab(
void ** ppSlab)
{
f_assert( ppSlab && *ppSlab);
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
if( m_uiTotalSlabs <= m_uiPreallocSlabs)
{
((SLABHEADER *)*ppSlab)->pPrev = NULL;
if( (((SLABHEADER *)*ppSlab)->pNext = m_pFirstInSlabList) != NULL)
{
((SLABHEADER *)m_pFirstInSlabList)->pPrev = *ppSlab;
}
else
{
m_pLastInSlabList = *ppSlab;
}
m_pFirstInSlabList = *ppSlab;
*ppSlab = NULL;
f_assert( m_uiInUseSlabs);
m_uiInUseSlabs--;
m_uiAvailSlabs++;
}
else
{
f_mutexUnlock( m_hMutex);
releaseSlabToSystem( *ppSlab);
*ppSlab = NULL;
f_mutexLock( m_hMutex);
f_assert( m_uiTotalSlabs);
f_assert( m_uiInUseSlabs);
f_assert( m_uiTotalBytesAllocated);
m_uiTotalSlabs--;
m_uiInUseSlabs--;
m_uiTotalBytesAllocated -= m_uiSlabSize;
}
f_mutexUnlock( m_hMutex);
}
/****************************************************************************
Desc: Assumes that the mutex is locked
****************************************************************************/
void F_SlabManager::freeAllSlabs( void)
{
void * pNextSlab;
SLABHEADER * pSlabHeader;
while( m_pFirstInSlabList)
{
pSlabHeader = (SLABHEADER *)m_pFirstInSlabList;
pNextSlab = pSlabHeader->pNext;
releaseSlabToSystem( m_pFirstInSlabList);
m_pFirstInSlabList = pNextSlab;
m_uiTotalSlabs--;
m_uiAvailSlabs--;
m_uiTotalBytesAllocated -= m_uiSlabSize;
}
f_assert( !m_uiAvailSlabs);
m_pLastInSlabList = NULL;
}
/****************************************************************************
Desc: Assumes that the mutex is locked
****************************************************************************/
void * F_SlabManager::allocSlabFromSystem( void)
{
void * pSlab;
#ifdef FLM_WIN
pSlab = VirtualAlloc( NULL,
(DWORD)m_uiSlabSize, MEM_COMMIT, PAGE_READWRITE);
#elif defined( FLM_UNIX) && !defined( FLM_SOLARIS)
if( (pSlab = mmap( 0, m_uiSlabSize,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED)
{
return( NULL);
}
// We don't use mmap on Solaris because of the amount of address
// space it consumes for red-zones and rounding. The following is from
// the Solaris mmap man page:
//
// The mmap() function aligns based on the length of the map-
// ping. When determining the amount of space to add to the
// address space, mmap() includes two 8-Kbyte pages, one at
// each end of the mapping that are not mapped and are there-
// fore used as "red-zone" pages. Attempts to reference these
// pages result in access violations.
//
// The size requested is incremented by the 16 Kbytes for these
// pages and is then subject to rounding constraints. The con-
// straints are:
//
// o For 32-bit processes:
//
// If length > 4 Mbytes
// round to 4-Mbyte multiple
// elseif length > 512 Kbytes
// round to 512-Kbyte multiple
// else
// round to 64-Kbyte multiple
//
// o For 64-bit processes:
//
// If length > 4 Mbytes
// round to 4-Mbyte multiple
// else
// round to 1-Mbyte multiple
//
// The net result is that for a 32-bit process:
//
// o If an mmap() request is made for 4 Mbytes, it results
// in 4 Mbytes + 16 Kbytes and is rounded up to 8 Mbytes.
//
// o If an mmap() request is made for 512 Kbytes, it results
// in 512 Kbytes + 16 Kbytes and is rounded up to 1 Mbyte.
//
// o If an mmap() request is made for 1 Mbyte, it results in
// 1 Mbyte + 16 Kbytes and is rounded up to 1.5 Mbytes.
//
// o Each 8-Kbyte mmap request "consumes" 64 Kbytes of vir-
// tual address space.
#else
if( RC_BAD( f_alloc( m_uiSlabSize, &pSlab)))
{
return( NULL);
}
#endif
return( pSlab);
}
/****************************************************************************
Desc: Assumes that the mutex is locked
****************************************************************************/
void F_SlabManager::releaseSlabToSystem(
void * pSlab)
{
f_assert( pSlab);
#ifdef FLM_WIN
VirtualFree( pSlab, 0, MEM_RELEASE);
#elif defined( FLM_UNIX) && !defined( FLM_SOLARIS)
munmap( pSlab, m_uiSlabSize);
#else
f_free( &pSlab);
#endif
}
/****************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI F_SlabManager::slabAddrCompareFunc(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2)
{
void * pSlab1 = (((void **)pvBuffer)[ uiPos1]);
void * pSlab2 = (((void **)pvBuffer)[ uiPos2]);
f_assert( pSlab1 != pSlab2);
if( pSlab1 < pSlab2)
{
return( -1);
}
return( 1);
}
/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI F_SlabManager::slabAddrSwapFunc(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2)
{
void ** ppSlab1 = &(((void **)pvBuffer)[ uiPos1]);
void ** ppSlab2 = &(((void **)pvBuffer)[ uiPos2]);
void * pTmp;
pTmp = *ppSlab1;
*ppSlab1 = *ppSlab2;
*ppSlab2 = pTmp;
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_SlabManager::sortSlabList( void)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiLoop;
void ** pSortBuf = NULL;
FLMUINT uiMaxSortEntries;
FLMUINT uiSortEntries = 0;
#define SMALL_SORT_BUF_SIZE 256
void * smallSortBuf[ SMALL_SORT_BUF_SIZE];
void * pCurSlab;
void * pPrevSib;
f_assertMutexLocked( m_hMutex);
if( m_uiAvailSlabs <= 1)
{
goto Exit;
}
uiMaxSortEntries = m_uiAvailSlabs;
// Sort the avail list according to the starting memory addresses of the
// slabs
if( uiMaxSortEntries <= SMALL_SORT_BUF_SIZE)
{
pSortBuf = smallSortBuf;
}
else
{
if( RC_BAD( rc = f_alloc( uiMaxSortEntries * sizeof( void *), &pSortBuf)))
{
goto Exit;
}
}
pCurSlab = m_pFirstInSlabList;
while( pCurSlab)
{
f_assert( uiSortEntries != uiMaxSortEntries);
pSortBuf[ uiSortEntries++] = pCurSlab;
pCurSlab = ((SLABHEADER *)pCurSlab)->pNext;
}
f_assert( uiSortEntries == uiMaxSortEntries);
// Quick sort
f_assert( uiSortEntries);
f_qsort( (FLMBYTE *)pSortBuf, 0, uiSortEntries - 1,
F_SlabManager::slabAddrCompareFunc,
F_SlabManager::slabAddrSwapFunc);
// Re-link the items in the list according to the new
// sort order
m_pFirstInSlabList = NULL;
m_pLastInSlabList = NULL;
pCurSlab = NULL;
pPrevSib = NULL;
for( uiLoop = 0; uiLoop < uiSortEntries; uiLoop++)
{
pCurSlab = pSortBuf[ uiLoop];
((SLABHEADER *)pCurSlab)->pNext = NULL;
((SLABHEADER *)pCurSlab)->pPrev = NULL;
if( pPrevSib)
{
((SLABHEADER *)pCurSlab)->pPrev = pPrevSib;
((SLABHEADER *)pPrevSib)->pNext = pCurSlab;
}
else
{
m_pFirstInSlabList = pCurSlab;
}
pPrevSib = pCurSlab;
}
m_pLastInSlabList = pCurSlab;
Exit:
if( pSortBuf && pSortBuf != smallSortBuf)
{
f_free( &pSortBuf);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
F_FixedAlloc::F_FixedAlloc()
{
m_pSlabManager = NULL;
m_pFirstSlab = NULL;
m_pLastSlab = NULL;
m_pDefaultRelocator = NULL;
m_pFirstSlabWithAvailCells = NULL;
m_pLastSlabWithAvailCells = NULL;
m_uiSlabsWithAvailCells = 0;
m_bAvailListSorted = TRUE;
m_uiTotalFreeCells = 0;
m_uiSlabSize = 0;
m_pUsageStats = NULL;
m_puiTotalBytesAllocated = NULL;
m_hMutex = F_MUTEX_NULL;
}
/****************************************************************************
Desc: Destructor for F_FixedAlloc. checks for memory leaks, and
frees all memory in use.
****************************************************************************/
F_FixedAlloc::~F_FixedAlloc()
{
#ifdef FLM_DEBUG
testForLeaks();
#endif
freeAll();
if( m_pSlabManager)
{
m_pSlabManager->Release();
}
if( m_pDefaultRelocator)
{
m_pDefaultRelocator->Release();
}
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &m_hMutex);
}
}
/****************************************************************************
Desc: Setup method for any setup that can fail
****************************************************************************/
RCODE F_FixedAlloc::setup(
FLMBOOL bMultiThreaded,
IF_SlabManager * pSlabManager,
IF_Relocator * pDefaultRelocator,
FLMUINT uiCellSize,
FLM_SLAB_USAGE * pUsageStats,
FLMUINT * puiTotalBytesAllocated)
{
RCODE rc = NE_FLM_OK;
f_assert( pSlabManager);
f_assert( uiCellSize);
f_assert( pUsageStats);
if( bMultiThreaded)
{
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
{
goto Exit;
}
}
m_pUsageStats = pUsageStats;
m_puiTotalBytesAllocated = puiTotalBytesAllocated;
m_pSlabManager = pSlabManager;
m_pSlabManager->AddRef();
if( pDefaultRelocator)
{
m_pDefaultRelocator = pDefaultRelocator;
m_pDefaultRelocator->AddRef();
}
m_uiCellSize = uiCellSize;
m_uiSlabSize = m_pSlabManager->getSlabSize();
// Get the alloc-aligned versions of all the sizes
m_uiSlabHeaderSize = getAllocAlignedSize( sizeof( SLAB));
if (pDefaultRelocator)
{
m_uiCellHeaderSize = getAllocAlignedSize( sizeof( CELLHEADER));
}
else
{
m_uiCellHeaderSize = getAllocAlignedSize( sizeof( CELLHEADER2));
}
m_uiCellSize = getAllocAlignedSize( m_uiCellSize);
// Ensure that there's enough space for our overhead
f_assert( m_uiCellSize >= sizeof( CELLAVAILNEXT));
m_uiSizeOfCellAndHeader = m_uiCellHeaderSize + m_uiCellSize;
m_uiCellsPerSlab =
(m_uiSlabSize - m_uiSlabHeaderSize) / m_uiSizeOfCellAndHeader;
f_assert( m_uiCellsPerSlab);
f_assert( m_uiCellsPerSlab <= FLM_MAX_UINT16);
f_assert( (m_uiCellsPerSlab * m_uiCellSize) < m_uiSlabSize);
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void * FLMAPI F_FixedAlloc::allocCell(
IF_Relocator * pRelocator,
void * pvInitialData,
FLMUINT uiDataSize)
{
void * pvCell;
FLMBOOL bMutexLocked = FALSE;
f_assert( pRelocator || m_pDefaultRelocator);
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( (pvCell = getCell( pRelocator)) == NULL)
{
goto Exit;
}
if( uiDataSize == sizeof( FLMUINT *))
{
*((FLMUINT *)pvCell) = *((FLMUINT *)pvInitialData);
}
else if( uiDataSize)
{
f_memcpy( pvCell, pvInitialData, uiDataSize);
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( pvCell);
}
/****************************************************************************
Desc: Public method to free a cell of memory back to the system.
****************************************************************************/
void * FLMAPI F_FixedAlloc::allocCell(
IF_Relocator * pRelocator,
F_ALLOC_INIT_FUNC fnAllocInit)
{
void * pvCell;
FLMBOOL bMutexLocked = FALSE;
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( (pvCell = getCell( pRelocator)) == NULL)
{
goto Exit;
}
if( pvCell && fnAllocInit)
{
fnAllocInit( pvCell, m_uiCellSize);
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( pvCell);
}
/****************************************************************************
Desc: Private, internal method to fetch a cell
****************************************************************************/
void * F_FixedAlloc::getCell(
IF_Relocator * pRelocator)
{
SLAB * pSlab = NULL;
FLMBYTE * pCell = NULL;
CELLHEADER * pHeader;
#ifdef FLM_DEBUG
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexLocked( m_hMutex);
}
#endif
// If there's a slab that has an avail cell, that one gets priority
if( (pSlab = m_pFirstSlabWithAvailCells) != NULL)
{
f_assert( pSlab->ui16AvailCellCount <= m_uiTotalFreeCells);
f_assert( m_uiTotalFreeCells);
f_assert( pSlab->ui16AllocatedCells < m_uiCellsPerSlab);
pCell = m_pFirstSlabWithAvailCells->pLocalAvailCellListHead;
f_assert( pCell);
pHeader = (CELLHEADER *)((FLMBYTE *)pCell - m_uiCellHeaderSize);
pSlab->ui16AllocatedCells++;
pSlab->ui16AvailCellCount--;
m_uiTotalFreeCells--;
// An avail cell holds as its contents the next pointer in the avail chain.
// Avail chains do not span slabs.
pSlab->pLocalAvailCellListHead = ((CELLAVAILNEXT *)pCell)->pNextInList;
// If there are no other avail cells in this slab at this point,
// then we need to unlink the slab from the
// slabs-with-avail-cells list, headed by m_pFirstSlabWithAvailCells
if( !pSlab->pLocalAvailCellListHead)
{
// Save a copy of the slab we're going to unlink
SLAB * pSlabToUnlink = pSlab;
// Need to keep the NULLNESS of the content of the cell consistent
// with the slab's ui16AvailCellCount being equal to 0
f_assert( !pSlabToUnlink->ui16AvailCellCount);
// There can't be a pPrevSlabWithAvailCells since
// we're positioned to the first one
f_assert( !pSlabToUnlink->pPrevSlabWithAvailCells);
// Update m_pFirstSlabWithAvailCells to point to the next one
if( (m_pFirstSlabWithAvailCells =
pSlabToUnlink->pNextSlabWithAvailCells) == NULL)
{
f_assert( m_pLastSlabWithAvailCells == pSlabToUnlink);
m_pLastSlabWithAvailCells = NULL;
}
// Unlink from slabs-with-avail-cells list
if( pSlabToUnlink->pNextSlabWithAvailCells)
{
pSlabToUnlink->pNextSlabWithAvailCells->pPrevSlabWithAvailCells =
pSlabToUnlink->pPrevSlabWithAvailCells;
pSlabToUnlink->pNextSlabWithAvailCells = NULL;
}
// Decrement the slab count
f_assert( m_uiSlabsWithAvailCells);
m_uiSlabsWithAvailCells--;
}
}
else
{
// If our m_pFirstSlab is completely full, or there is no
// m_pFirstSlab, it is time to allocate a new slab
if( !m_pFirstSlab ||
(m_pFirstSlab->ui16NextNeverUsedCell == m_uiCellsPerSlab))
{
SLAB * pNewSlab;
if( (pNewSlab = getAnotherSlab()) == NULL)
{
goto Exit;
}
if( m_pFirstSlab)
{
pNewSlab->pNext = m_pFirstSlab;
m_pFirstSlab->pPrev = pNewSlab;
}
else
{
m_pLastSlab = pNewSlab;
}
m_pFirstSlab = pNewSlab;
}
pSlab = m_pFirstSlab;
pSlab->ui16AllocatedCells++;
pHeader = (CELLHEADER *)
((FLMBYTE *)pSlab + m_uiSlabHeaderSize +
(m_uiSizeOfCellAndHeader * m_pFirstSlab->ui16NextNeverUsedCell));
pCell = ((FLMBYTE *)pHeader + m_uiCellHeaderSize);
m_pFirstSlab->ui16NextNeverUsedCell++;
}
pHeader->pContainingSlab = pSlab;
#ifdef FLM_DEBUG
if (gv_bTrackLeaks && gv_bStackWalk)
{
pHeader->puiStack = memWalkStack();
}
else
{
pHeader->puiStack = NULL;
}
#endif
if (!m_pDefaultRelocator)
{
((CELLHEADER2 *)pHeader)->pRelocator = pRelocator;
}
if( m_pUsageStats)
{
m_pUsageStats->ui64AllocatedCells++;
}
Exit:
return( pCell);
}
/****************************************************************************
Desc: Public method to free a cell of memory back to the system.
****************************************************************************/
void FLMAPI F_FixedAlloc::freeCell(
void * ptr)
{
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
}
freeCell( ptr, FALSE, NULL);
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexUnlock( m_hMutex);
}
}
/****************************************************************************
Desc: Public method to free a cell of memory back to the system.
****************************************************************************/
void F_FixedAlloc::freeCell(
void * pCell,
FLMBOOL bFreeIfEmpty,
FLMBOOL * pbFreedSlab)
{
CELLAVAILNEXT * pCellContents;
CELLHEADER * pHeader;
SLAB * pSlab;
#ifdef FLM_DEBUG
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexLocked( m_hMutex);
}
#endif
if( pbFreedSlab)
{
*pbFreedSlab = FALSE;
}
if( !pCell)
{
return;
}
pCellContents = (CELLAVAILNEXT *)pCell;
pHeader = (CELLHEADER *)(((FLMBYTE *)pCell) - m_uiCellHeaderSize);
pSlab = pHeader->pContainingSlab;
// Memory corruption detected!
if( !pSlab || pSlab->pvAllocator != (void *)this)
{
f_assert( 0);
goto Exit;
}
pHeader->pContainingSlab = NULL;
#ifdef FLM_DEBUG
if( pHeader->puiStack)
{
os_free( pHeader->puiStack);
pHeader->puiStack = NULL;
}
#endif
// Should always be set on a free
f_assert( m_pFirstSlab);
// Add the cell to the pSlab's free list
pCellContents->pNextInList = pSlab->pLocalAvailCellListHead;
#ifdef FLM_DEBUG
// Write out a string that's easy to see in memory when debugging
f_strcpy( (char *)pCellContents->szDebugPattern, "FREECELL");
#endif
f_assert( pCell);
pSlab->pLocalAvailCellListHead = (FLMBYTE *)pCell;
pSlab->ui16AvailCellCount++;
f_assert( pSlab->ui16AllocatedCells);
pSlab->ui16AllocatedCells--;
// If there's no chain, make this one the first
if( !m_pFirstSlabWithAvailCells)
{
m_pFirstSlabWithAvailCells = pSlab;
m_pLastSlabWithAvailCells = pSlab;
f_assert( !pSlab->pNextSlabWithAvailCells);
f_assert( !pSlab->pPrevSlabWithAvailCells);
m_uiSlabsWithAvailCells++;
m_bAvailListSorted = TRUE;
}
else if( pSlab->ui16AvailCellCount == 1)
{
// This item is not linked in to the chain, so link it in
if( m_bAvailListSorted && pSlab > m_pFirstSlabWithAvailCells)
{
m_bAvailListSorted = FALSE;
}
pSlab->pNextSlabWithAvailCells = m_pFirstSlabWithAvailCells;
pSlab->pPrevSlabWithAvailCells = NULL;
m_pFirstSlabWithAvailCells->pPrevSlabWithAvailCells = pSlab;
m_pFirstSlabWithAvailCells = pSlab;
m_uiSlabsWithAvailCells++;
}
// Adjust counter, because the cell is now considered free
m_uiTotalFreeCells++;
// If this slab is now totally avail
if( pSlab->ui16AvailCellCount == m_uiCellsPerSlab)
{
f_assert( !pSlab->ui16AllocatedCells);
// If we have met our threshold for being able to free a slab
if( m_uiTotalFreeCells >= m_uiCellsPerSlab || bFreeIfEmpty)
{
freeSlab( pSlab);
if( pbFreedSlab)
{
*pbFreedSlab = TRUE;
}
}
else if( pSlab != m_pFirstSlabWithAvailCells)
{
// Link the slab to the front of the avail list so that
// it can be freed quickly at some point in the future
if( pSlab->pPrevSlabWithAvailCells)
{
pSlab->pPrevSlabWithAvailCells->pNextSlabWithAvailCells =
pSlab->pNextSlabWithAvailCells;
}
if( pSlab->pNextSlabWithAvailCells)
{
pSlab->pNextSlabWithAvailCells->pPrevSlabWithAvailCells =
pSlab->pPrevSlabWithAvailCells;
}
else
{
f_assert( m_pLastSlabWithAvailCells == pSlab);
m_pLastSlabWithAvailCells = pSlab->pPrevSlabWithAvailCells;
}
if( m_pFirstSlabWithAvailCells)
{
m_pFirstSlabWithAvailCells->pPrevSlabWithAvailCells = pSlab;
}
pSlab->pPrevSlabWithAvailCells = NULL;
pSlab->pNextSlabWithAvailCells = m_pFirstSlabWithAvailCells;
m_pFirstSlabWithAvailCells = pSlab;
}
}
if( m_pUsageStats)
{
m_pUsageStats->ui64AllocatedCells--;
}
Exit:
return;
}
/****************************************************************************
Desc: Grabs another slab of memory from the operating system
****************************************************************************/
SLAB * F_FixedAlloc::getAnotherSlab( void)
{
SLAB * pSlab = NULL;
#ifdef FLM_DEBUG
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexLocked( m_hMutex);
}
#endif
if( RC_BAD( m_pSlabManager->allocSlab( (void **)&pSlab)))
{
goto Exit;
}
// Initialize the slab header fields
f_memset( pSlab, 0, sizeof( SLAB));
pSlab->pvAllocator = (void *)this;
if( m_pUsageStats)
{
m_pUsageStats->ui64Slabs++;
}
if( m_puiTotalBytesAllocated)
{
(*m_puiTotalBytesAllocated) += m_pSlabManager->getSlabSize();
}
Exit:
return( pSlab);
}
/****************************************************************************
Desc: Private internal method to free an unused empty slab back to the OS.
****************************************************************************/
void F_FixedAlloc::freeSlab(
SLAB * pSlab)
{
#ifdef FLM_DEBUG
CELLAVAILNEXT * pAvailNext = NULL;
FLMUINT32 ui32AvailCount = 0;
#endif
f_assert( pSlab);
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexLocked( m_hMutex);
}
// Memory corruption detected!
if( pSlab->ui16AllocatedCells || pSlab->pvAllocator != this)
{
f_assert( 0);
return;
}
#ifdef FLM_DEBUG
// Walk the avail chain as a sanity check
pAvailNext = (CELLAVAILNEXT *)pSlab->pLocalAvailCellListHead;
while( pAvailNext)
{
ui32AvailCount++;
pAvailNext = (CELLAVAILNEXT *)pAvailNext->pNextInList;
}
f_assert( pSlab->ui16AvailCellCount == ui32AvailCount);
f_assert( pSlab->ui16NextNeverUsedCell >= ui32AvailCount);
#endif
// Unlink from all-slabs-list
if( pSlab->pNext)
{
pSlab->pNext->pPrev = pSlab->pPrev;
}
else
{
m_pLastSlab = pSlab->pPrev;
}
if( pSlab->pPrev)
{
pSlab->pPrev->pNext = pSlab->pNext;
}
else
{
m_pFirstSlab = pSlab->pNext;
}
// Unlink from slabs-with-avail-cells list
if( pSlab->pNextSlabWithAvailCells)
{
pSlab->pNextSlabWithAvailCells->pPrevSlabWithAvailCells =
pSlab->pPrevSlabWithAvailCells;
}
else
{
m_pLastSlabWithAvailCells = pSlab->pPrevSlabWithAvailCells;
}
if( pSlab->pPrevSlabWithAvailCells)
{
pSlab->pPrevSlabWithAvailCells->pNextSlabWithAvailCells =
pSlab->pNextSlabWithAvailCells;
}
else
{
m_pFirstSlabWithAvailCells = pSlab->pNextSlabWithAvailCells;
}
f_assert( m_uiSlabsWithAvailCells);
m_uiSlabsWithAvailCells--;
f_assert( m_uiTotalFreeCells >= pSlab->ui16AvailCellCount);
m_uiTotalFreeCells -= pSlab->ui16AvailCellCount;
m_pSlabManager->freeSlab( (void **)&pSlab);
if( m_pUsageStats)
{
f_assert( m_pUsageStats->ui64Slabs);
m_pUsageStats->ui64Slabs--;
}
if( m_puiTotalBytesAllocated)
{
f_assert( (*m_puiTotalBytesAllocated) >= m_pSlabManager->getSlabSize());
(*m_puiTotalBytesAllocated) -= m_pSlabManager->getSlabSize();
}
}
/****************************************************************************
Desc: Public method to free all the memory in the system.
****************************************************************************/
void FLMAPI F_FixedAlloc::freeAll( void)
{
SLAB * pFreeMe;
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
}
while( m_pFirstSlab)
{
pFreeMe = m_pFirstSlab;
m_pFirstSlab = m_pFirstSlab->pNext;
freeSlab( pFreeMe);
}
f_assert( !m_uiTotalFreeCells);
m_pFirstSlab = NULL;
m_pLastSlab = NULL;
m_pFirstSlabWithAvailCells = NULL;
m_pLastSlabWithAvailCells = NULL;
m_uiSlabsWithAvailCells = 0;
m_bAvailListSorted = TRUE;
m_uiTotalFreeCells = 0;
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexUnlock( m_hMutex);
}
}
/****************************************************************************
Desc: If a relocation callback function has been registered, and memory
can be compressed, the avail list will be compressed
****************************************************************************/
void F_FixedAlloc::defragmentMemory( void)
{
SLAB * pCurSlab;
SLAB * pPrevSib;
CELLHEADER * pCellHeader;
FLMBOOL bSlabFreed;
FLMBYTE * pucOriginal;
FLMBYTE * pucReloc = NULL;
FLMUINT uiLoop;
SLAB ** pSortBuf = NULL;
FLMUINT uiMaxSortEntries;
FLMUINT uiSortEntries = 0;
#define SMALL_SORT_BUF_SIZE 256
SLAB * smallSortBuf[ SMALL_SORT_BUF_SIZE];
FLMBOOL bMutexLocked = FALSE;
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( m_uiTotalFreeCells < m_uiCellsPerSlab)
{
goto Exit;
}
uiMaxSortEntries = m_uiSlabsWithAvailCells;
// Re-sort the slabs in the avail list according to
// their memory addresses to help reduce logical fragmentation
if( !m_bAvailListSorted && uiMaxSortEntries > 1)
{
if( uiMaxSortEntries <= SMALL_SORT_BUF_SIZE)
{
pSortBuf = smallSortBuf;
}
else
{
if( RC_BAD( f_alloc( uiMaxSortEntries * sizeof( SLAB *), &pSortBuf)))
{
goto Exit;
}
}
pCurSlab = m_pFirstSlabWithAvailCells;
while( pCurSlab)
{
f_assert( uiSortEntries != uiMaxSortEntries);
pSortBuf[ uiSortEntries++] = pCurSlab;
pCurSlab = pCurSlab->pNextSlabWithAvailCells;
}
// Quick sort
f_assert( uiSortEntries);
f_qsort( (FLMBYTE *)pSortBuf, 0, uiSortEntries - 1,
F_FixedAlloc::slabAddrCompareFunc,
F_FixedAlloc::slabAddrSwapFunc);
// Re-link the items in the list according to the new
// sort order
m_pFirstSlabWithAvailCells = NULL;
m_pLastSlabWithAvailCells = NULL;
pCurSlab = NULL;
pPrevSib = NULL;
for( uiLoop = 0; uiLoop < uiSortEntries; uiLoop++)
{
pCurSlab = pSortBuf[ uiLoop];
pCurSlab->pNextSlabWithAvailCells = NULL;
pCurSlab->pPrevSlabWithAvailCells = NULL;
if( pPrevSib)
{
pCurSlab->pPrevSlabWithAvailCells = pPrevSib;
pPrevSib->pNextSlabWithAvailCells = pCurSlab;
}
else
{
m_pFirstSlabWithAvailCells = pCurSlab;
}
pPrevSib = pCurSlab;
}
m_pLastSlabWithAvailCells = pCurSlab;
m_bAvailListSorted = TRUE;
}
// Process the avail list (which should be sorted unless
// we are too low on memory)
pCurSlab = m_pLastSlabWithAvailCells;
while( pCurSlab)
{
if( m_uiTotalFreeCells < m_uiCellsPerSlab)
{
// No need to continue ... we aren't above the
// free cell threshold
goto Exit;
}
pPrevSib = pCurSlab->pPrevSlabWithAvailCells;
if( pCurSlab == m_pFirstSlabWithAvailCells ||
!pCurSlab->ui16AvailCellCount)
{
// We've either hit the beginning of the avail list or
// the slab that we are now positioned on has been
// removed from the avail list. In either case,
// we are done.
break;
}
if( pCurSlab->ui16AvailCellCount == m_uiCellsPerSlab ||
pCurSlab->ui16NextNeverUsedCell == pCurSlab->ui16AvailCellCount)
{
freeSlab( pCurSlab);
pCurSlab = pPrevSib;
continue;
}
for( uiLoop = 0; uiLoop < pCurSlab->ui16NextNeverUsedCell &&
pCurSlab != m_pFirstSlabWithAvailCells &&
m_uiTotalFreeCells >= m_uiCellsPerSlab; uiLoop++)
{
IF_Relocator * pRelocator;
pCellHeader = (CELLHEADER *)
((FLMBYTE *)pCurSlab + m_uiSlabHeaderSize +
(uiLoop * m_uiSizeOfCellAndHeader));
if ((pRelocator = m_pDefaultRelocator) == NULL)
{
pRelocator = ((CELLHEADER2 *)pCellHeader)->pRelocator;
}
if( pCellHeader->pContainingSlab)
{
// If pContainingSlab is non-NULL, the cell is currently allocated
f_assert( pCellHeader->pContainingSlab == pCurSlab);
pucOriginal = ((FLMBYTE *)pCellHeader + m_uiCellHeaderSize);
if( pRelocator->canRelocate( pucOriginal))
{
if( (pucReloc = (FLMBYTE *)getCell( pRelocator)) == NULL)
{
goto Exit;
}
f_memcpy( pucReloc, pucOriginal, m_uiCellSize);
pRelocator->relocate( pucOriginal, pucReloc);
freeCell( pucOriginal, TRUE, &bSlabFreed);
if( bSlabFreed)
{
break;
}
}
}
}
pCurSlab = pPrevSib;
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
if( pSortBuf && pSortBuf != smallSortBuf)
{
f_free( &pSortBuf);
}
}
/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI F_FixedAlloc::freeUnused( void)
{
SLAB * pSlab;
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
}
if( (pSlab = m_pFirstSlabWithAvailCells) != NULL &&
!pSlab->ui16AllocatedCells)
{
freeSlab( pSlab);
}
if( (pSlab = m_pFirstSlab) != NULL &&
!pSlab->ui16AllocatedCells)
{
freeSlab( pSlab);
}
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexUnlock( m_hMutex);
}
}
/****************************************************************************
Desc: Debug method to do mem leak testing. Any cells allocated via
allocCell but not freed via freeCell() will be triggered here.
****************************************************************************/
#ifdef FLM_DEBUG
void F_FixedAlloc::testForLeaks( void)
{
SLAB * pSlabRover = m_pFirstSlab;
CELLHEADER * pHeader;
FLMUINT uiLoop;
F_MEM_HDR memHeader;
// Test for leaks
while( pSlabRover)
{
for( uiLoop = 0; uiLoop < pSlabRover->ui16NextNeverUsedCell; uiLoop++)
{
pHeader = (CELLHEADER *)
((FLMBYTE *)pSlabRover + m_uiSlabHeaderSize +
(uiLoop * m_uiSizeOfCellAndHeader));
// Nonzero here means we have a leak
if( pHeader->pContainingSlab)
{
// We have a leak, so let's call logMemLeak with the
// appropriate header passed in
f_memset( &memHeader, 0, sizeof( F_MEM_HDR));
memHeader.uiDataSize = m_uiCellSize;
memHeader.puiStack = pHeader->puiStack;
logMemLeak( &memHeader);
}
}
pSlabRover = pSlabRover->pNext;
}
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
F_BufferAlloc::~F_BufferAlloc()
{
FLMUINT uiLoop;
for (uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++)
{
if( m_ppAllocators[ uiLoop])
{
m_ppAllocators[ uiLoop]->Release();
m_ppAllocators[ uiLoop] = NULL;
}
}
if( m_pSlabManager)
{
m_pSlabManager->Release();
}
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &m_hMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_BufferAlloc::setup(
FLMBOOL bMultiThreaded,
IF_SlabManager * pSlabManager,
IF_Relocator * pDefaultRelocator,
FLM_SLAB_USAGE * pUsageStats,
FLMUINT * puiTotalBytesAllocated)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiLoop;
FLMUINT uiSize;
f_assert( pSlabManager);
if( bMultiThreaded)
{
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
{
goto Exit;
}
}
m_pSlabManager = pSlabManager;
m_pSlabManager->AddRef();
for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++)
{
if( (m_ppAllocators[ uiLoop] = f_new F_FixedAlloc) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
switch (uiLoop)
{
case 0:
uiSize = CELL_SIZE_0;
break;
case 1:
uiSize = CELL_SIZE_1;
break;
case 2:
uiSize = CELL_SIZE_2;
break;
case 3:
uiSize = CELL_SIZE_3;
break;
case 4:
uiSize = CELL_SIZE_4;
break;
case 5:
uiSize = CELL_SIZE_5;
break;
case 6:
uiSize = CELL_SIZE_6;
break;
case 7:
uiSize = CELL_SIZE_7;
break;
case 8:
uiSize = CELL_SIZE_8;
break;
case 9:
uiSize = CELL_SIZE_9;
break;
case 10:
uiSize = CELL_SIZE_10;
break;
case 11:
uiSize = CELL_SIZE_11;
break;
case 12:
uiSize = CELL_SIZE_12;
break;
case 13:
uiSize = CELL_SIZE_13;
break;
case 14:
uiSize = CELL_SIZE_14;
break;
case 15:
uiSize = CELL_SIZE_15;
break;
case 16:
uiSize = CELL_SIZE_16;
break;
case 17:
uiSize = CELL_SIZE_17;
break;
case 18:
uiSize = CELL_SIZE_18;
break;
case 19:
uiSize = CELL_SIZE_19;
break;
case 20:
uiSize = CELL_SIZE_20;
break;
case 21:
uiSize = CELL_SIZE_21;
break;
default:
uiSize = 0;
rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED);
goto Exit;
}
if (RC_BAD( rc = m_ppAllocators[ uiLoop]->setup( FALSE,
pSlabManager, pDefaultRelocator, uiSize,
pUsageStats, puiTotalBytesAllocated)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_BufferAlloc::allocBuf(
IF_Relocator * pRelocator,
FLMUINT uiSize,
void * pvInitialData,
FLMUINT uiDataSize,
FLMBYTE ** ppucBuffer,
FLMBOOL * pbAllocatedOnHeap)
{
RCODE rc = NE_FLM_OK;
IF_FixedAlloc * pAllocator = getAllocator( uiSize);
FLMBOOL bMutexLocked = FALSE;
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = FALSE;
}
if( pAllocator)
{
f_assert( pAllocator->getCellSize() >= uiSize);
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell( pRelocator,
pvInitialData, uiDataSize)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
}
else
{
if( RC_BAD( rc = f_alloc( uiSize, ppucBuffer)))
{
goto Exit;
}
m_pSlabManager->incrementTotalBytesAllocated( f_msize( *ppucBuffer));
if( pvInitialData)
{
f_memcpy( *ppucBuffer, pvInitialData, uiDataSize);
}
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = TRUE;
}
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_BufferAlloc::allocBuf(
IF_Relocator * pRelocator,
FLMUINT uiSize,
F_ALLOC_INIT_FUNC fnAllocInit,
FLMBYTE ** ppucBuffer,
FLMBOOL * pbAllocatedOnHeap)
{
RCODE rc = NE_FLM_OK;
IF_FixedAlloc * pAllocator = getAllocator( uiSize);
FLMBOOL bMutexLocked = FALSE;
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = FALSE;
}
if( pAllocator)
{
f_assert( pAllocator->getCellSize() >= uiSize);
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell(
pRelocator, fnAllocInit)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
}
else
{
if( RC_BAD( rc = f_alloc( uiSize, ppucBuffer)))
{
goto Exit;
}
m_pSlabManager->incrementTotalBytesAllocated( f_msize( *ppucBuffer));
if( fnAllocInit)
{
fnAllocInit( *ppucBuffer, uiSize);
}
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = TRUE;
}
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_BufferAlloc::reallocBuf(
IF_Relocator * pRelocator,
FLMUINT uiOldSize,
FLMUINT uiNewSize,
void * pvInitialData,
FLMUINT uiDataSize,
FLMBYTE ** ppucBuffer,
FLMBOOL * pbAllocatedOnHeap)
{
RCODE rc = NE_FLM_OK;
FLMBYTE * pucTmp;
IF_FixedAlloc * pOldAllocator;
IF_FixedAlloc * pNewAllocator;
FLMBOOL bMutexLocked = FALSE;
f_assert( uiNewSize);
if( !uiOldSize)
{
rc = allocBuf( pRelocator, uiNewSize, pvInitialData, uiDataSize,
ppucBuffer, pbAllocatedOnHeap);
goto Exit;
}
pOldAllocator = getAllocator( uiOldSize);
pNewAllocator = getAllocator( uiNewSize);
if( pOldAllocator && pOldAllocator == pNewAllocator)
{
// The allocation will still fit in the same cell
goto Exit;
}
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = FALSE;
}
if( pOldAllocator)
{
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( pNewAllocator)
{
f_assert( pOldAllocator != pNewAllocator);
if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell( pRelocator,
NULL, 0)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
}
else
{
if( RC_BAD( rc = f_alloc( uiNewSize, &pucTmp)))
{
goto Exit;
}
m_pSlabManager->incrementTotalBytesAllocated( f_msize( pucTmp));
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = TRUE;
}
}
f_memcpy( pucTmp, *ppucBuffer, f_min( uiOldSize, uiNewSize));
pOldAllocator->freeCell( *ppucBuffer);
*ppucBuffer = pucTmp;
}
else
{
if( pNewAllocator)
{
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell( pRelocator,
*ppucBuffer, f_min( uiOldSize, uiNewSize))) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
bMutexLocked = FALSE;
}
m_pSlabManager->decrementTotalBytesAllocated( f_msize( *ppucBuffer));
f_free( ppucBuffer);
*ppucBuffer = pucTmp;
}
else
{
FLMUINT uiOldAllocSize = f_msize( *ppucBuffer);
f_assert( uiOldSize > m_ppAllocators[ NUM_BUF_ALLOCATORS - 1]->getCellSize());
f_assert( uiNewSize > m_ppAllocators[ NUM_BUF_ALLOCATORS - 1]->getCellSize());
if( RC_BAD( rc = f_realloc( uiNewSize, ppucBuffer)))
{
goto Exit;
}
m_pSlabManager->decrementTotalBytesAllocated( uiOldAllocSize);
m_pSlabManager->incrementTotalBytesAllocated( f_msize( *ppucBuffer));
if( pbAllocatedOnHeap)
{
*pbAllocatedOnHeap = TRUE;
}
}
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_BufferAlloc::freeBuf(
FLMUINT uiSize,
FLMBYTE ** ppucBuffer)
{
IF_FixedAlloc * pAllocator = getAllocator( uiSize);
FLMBOOL bMutexLocked = FALSE;
if( pAllocator)
{
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
pAllocator->freeCell( *ppucBuffer);
*ppucBuffer = NULL;
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
}
else
{
m_pSlabManager->decrementTotalBytesAllocated( f_msize( *ppucBuffer));
f_free( ppucBuffer);
}
}
/****************************************************************************
Desc:
****************************************************************************/
void F_BufferAlloc::defragmentMemory( void)
{
FLMUINT uiLoop;
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
}
for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++)
{
if( m_ppAllocators[ uiLoop])
{
m_ppAllocators[ uiLoop]->defragmentMemory();
m_ppAllocators[ uiLoop]->freeUnused();
}
uiLoop++;
}
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexUnlock( m_hMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT F_BufferAlloc::getTrueSize(
FLMUINT uiSize,
FLMBYTE * pucBuffer)
{
FLMUINT uiTrueSize;
IF_FixedAlloc * pAllocator;
if( !uiSize)
{
uiTrueSize = 0;
}
else if( (pAllocator = getAllocator( uiSize)) != NULL)
{
uiTrueSize = pAllocator->getCellSize();
}
else
{
uiTrueSize = f_msize( pucBuffer);
}
return( uiTrueSize);
}
/****************************************************************************
Desc:
****************************************************************************/
IF_FixedAlloc * F_BufferAlloc::getAllocator(
FLMUINT uiSize)
{
IF_FixedAlloc * pAllocator;
f_assert( uiSize);
if( uiSize <= CELL_SIZE_10)
{
if( uiSize <= CELL_SIZE_4)
{
if( uiSize <= CELL_SIZE_2)
{
if( uiSize <= CELL_SIZE_0)
{
pAllocator = m_ppAllocators [0];
}
else
{
pAllocator = (uiSize <= CELL_SIZE_1
? m_ppAllocators [1]
: m_ppAllocators [2]);
}
}
else
{
pAllocator = (uiSize <= CELL_SIZE_3
? m_ppAllocators [3]
: m_ppAllocators [4]);
}
}
else if( uiSize <= CELL_SIZE_7)
{
if( uiSize <= CELL_SIZE_5)
{
pAllocator = m_ppAllocators [5];
}
else
{
pAllocator = (uiSize <= CELL_SIZE_6
? m_ppAllocators [6]
: m_ppAllocators [7]);
}
}
else
{
if( uiSize <= CELL_SIZE_8)
{
pAllocator = m_ppAllocators [8];
}
else
{
pAllocator = (uiSize <= CELL_SIZE_9
? m_ppAllocators [9]
: m_ppAllocators [10]);
}
}
}
else if( uiSize <= CELL_SIZE_16)
{
if( uiSize <= CELL_SIZE_13)
{
if( uiSize <= CELL_SIZE_11)
{
pAllocator = m_ppAllocators [11];
}
else
{
pAllocator = (uiSize <= CELL_SIZE_12
? m_ppAllocators [12]
: m_ppAllocators [13]);
}
}
else
{
if( uiSize <= CELL_SIZE_14)
{
pAllocator = m_ppAllocators [14];
}
else
{
pAllocator = (uiSize <= CELL_SIZE_15
? m_ppAllocators [15]
: m_ppAllocators [16]);
}
}
}
else if( uiSize <= CELL_SIZE_19)
{
if( uiSize <= CELL_SIZE_17)
{
pAllocator = m_ppAllocators [17];
}
else
{
pAllocator = (uiSize <= CELL_SIZE_18
? m_ppAllocators [18]
: m_ppAllocators [19]);
}
}
else if( uiSize <= CELL_SIZE_21)
{
pAllocator = (uiSize <= CELL_SIZE_20
? m_ppAllocators [20]
: m_ppAllocators [21]);
}
else
{
pAllocator = NULL;
}
return( pAllocator);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_MultiAlloc::setup(
FLMBOOL bMultiThreaded,
IF_SlabManager * pSlabManager,
IF_Relocator * pDefaultRelocator,
FLMUINT * puiCellSizes,
FLM_SLAB_USAGE * pUsageStats,
FLMUINT * puiTotalBytesAllocated)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiLoop;
FLMUINT uiCellCount;
if( bMultiThreaded)
{
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
{
goto Exit;
}
}
m_pSlabManager = pSlabManager;
m_pSlabManager->AddRef();
uiCellCount = 0;
while( puiCellSizes[ uiCellCount])
{
uiCellCount++;
}
if( !uiCellCount)
{
rc = RC_SET_AND_ASSERT( NE_FLM_INVALID_PARM);
goto Exit;
}
f_qsort( puiCellSizes, 0, uiCellCount - 1,
f_qsortUINTCompare, f_qsortUINTSwap);
if( RC_BAD( rc = f_alloc(
sizeof( FLMUINT *) * (uiCellCount + 1), &m_puiCellSizes)))
{
goto Exit;
}
m_pSlabManager->incrementTotalBytesAllocated( f_msize( m_puiCellSizes));
f_memcpy( m_puiCellSizes, puiCellSizes,
(uiCellCount + 1) * sizeof( FLMUINT));
// Set up the allocators
if( RC_BAD( rc = f_calloc(
sizeof( F_FixedAlloc *) * (uiCellCount + 1), &m_ppAllocators)))
{
goto Exit;
}
m_pSlabManager->incrementTotalBytesAllocated( f_msize( m_ppAllocators));
uiLoop = 0;
while( m_puiCellSizes[ uiLoop])
{
if( (m_ppAllocators[ uiLoop] = f_new F_FixedAlloc) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
if( RC_BAD( rc = m_ppAllocators[ uiLoop]->setup( FALSE,
pSlabManager, pDefaultRelocator, m_puiCellSizes[ uiLoop],
pUsageStats, puiTotalBytesAllocated)))
{
goto Exit;
}
uiLoop++;
}
Exit:
if( RC_BAD( rc))
{
cleanup();
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_MultiAlloc::cleanup( void)
{
FLMUINT uiLoop = 0;
if( m_puiCellSizes && m_ppAllocators)
{
while( m_puiCellSizes[ uiLoop])
{
if( m_ppAllocators[ uiLoop])
{
m_ppAllocators[ uiLoop]->Release();
m_ppAllocators[ uiLoop] = NULL;
}
uiLoop++;
}
}
if( m_puiCellSizes)
{
m_pSlabManager->decrementTotalBytesAllocated( f_msize( m_puiCellSizes));
f_free( &m_puiCellSizes);
}
if( m_ppAllocators)
{
m_pSlabManager->decrementTotalBytesAllocated( f_msize( m_ppAllocators));
f_free( &m_ppAllocators);
}
if( m_pSlabManager)
{
m_pSlabManager->Release();
m_pSlabManager = NULL;
}
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &m_hMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_MultiAlloc::allocBuf(
IF_Relocator * pRelocator,
FLMUINT uiSize,
FLMBYTE ** ppucBuffer)
{
RCODE rc = NE_FLM_OK;
IF_FixedAlloc * pAllocator = getAllocator( uiSize);
FLMBOOL bMutexLocked = FALSE;
f_assert( pAllocator);
f_assert( pAllocator->getCellSize() >= uiSize);
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell(
pRelocator, NULL, 0)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_MultiAlloc::allocBuf(
IF_Relocator * pRelocator,
FLMUINT uiSize,
F_ALLOC_INIT_FUNC fnAllocInit,
FLMBYTE ** ppucBuffer)
{
RCODE rc = NE_FLM_OK;
IF_FixedAlloc * pAllocator = getAllocator( uiSize);
FLMBOOL bMutexLocked = FALSE;
f_assert( pAllocator);
f_assert( pAllocator->getCellSize() >= uiSize);
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell(
pRelocator, fnAllocInit)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_MultiAlloc::reallocBuf(
IF_Relocator * pRelocator,
FLMUINT uiNewSize,
FLMBYTE ** ppucBuffer)
{
RCODE rc = NE_FLM_OK;
FLMBYTE * pucTmp;
IF_FixedAlloc * pOldAllocator;
IF_FixedAlloc * pNewAllocator;
FLMBOOL bMutexLocked = FALSE;
f_assert( uiNewSize);
if( !(*ppucBuffer))
{
rc = allocBuf( pRelocator, uiNewSize, ppucBuffer);
goto Exit;
}
pOldAllocator = getAllocator( *ppucBuffer);
pNewAllocator = getAllocator( uiNewSize);
if( pOldAllocator == pNewAllocator)
{
// The allocation will still fit in the same cell
goto Exit;
}
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
}
if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell( pRelocator, *ppucBuffer,
f_min( uiNewSize, pOldAllocator->getCellSize()))) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
pOldAllocator->freeCell( *ppucBuffer);
*ppucBuffer = pucTmp;
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI F_MultiAlloc::freeBuf(
FLMBYTE ** ppucBuffer)
{
if( ppucBuffer && *ppucBuffer)
{
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
}
getAllocator( *ppucBuffer)->freeCell( *ppucBuffer);
*ppucBuffer = NULL;
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexUnlock( m_hMutex);
}
}
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI F_MultiAlloc::getTrueSize(
FLMBYTE * pucBuffer)
{
FLMUINT uiSize;
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
}
uiSize = getAllocator( pucBuffer)->getCellSize();
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexUnlock( m_hMutex);
}
return( uiSize);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_MultiAlloc::defragmentMemory( void)
{
FLMUINT uiLoop = 0;
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexNotLocked( m_hMutex);
f_mutexLock( m_hMutex);
}
while( m_puiCellSizes[ uiLoop])
{
if( m_ppAllocators[ uiLoop])
{
m_ppAllocators[ uiLoop]->defragmentMemory();
m_ppAllocators[ uiLoop]->freeUnused();
}
uiLoop++;
}
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexUnlock( m_hMutex);
}
}
/****************************************************************************
Desc:
****************************************************************************/
IF_FixedAlloc * F_MultiAlloc::getAllocator(
FLMUINT uiSize)
{
IF_FixedAlloc * pAllocator = NULL;
FLMUINT uiLoop;
f_assert( uiSize);
for( uiLoop = 0; m_puiCellSizes[ uiLoop]; uiLoop++)
{
if( m_puiCellSizes[ uiLoop] >= uiSize)
{
pAllocator = m_ppAllocators[ uiLoop];
break;
}
}
return( pAllocator);
}
/****************************************************************************
Desc:
****************************************************************************/
IF_FixedAlloc * F_MultiAlloc::getAllocator(
FLMBYTE * pucBuffer)
{
CELLHEADER * pHeader;
SLAB * pSlab;
IF_FixedAlloc * pAllocator = NULL;
#ifdef FLM_DEBUG
if( m_hMutex != F_MUTEX_NULL)
{
f_assertMutexLocked( m_hMutex);
}
#endif
pHeader = (CELLHEADER *)(pucBuffer -
F_FixedAlloc::getAllocAlignedSize( sizeof( CELLHEADER2)));
pSlab = pHeader->pContainingSlab;
pAllocator = (IF_FixedAlloc *)pSlab->pvAllocator;
return( pAllocator);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_allocAlignedBufferImp(
FLMUINT uiMinSize,
void ** ppvAlloc)
{
RCODE rc = NE_FLM_OK;
#ifdef FLM_WIN
if ((*ppvAlloc = (void *)VirtualAlloc( NULL,
uiMinSize, MEM_COMMIT, PAGE_READWRITE)) == NULL)
{
rc = f_mapPlatformError( GetLastError(), NE_FLM_MEM);
goto Exit;
}
#elif defined( FLM_SOLARIS)
if( (*ppvAlloc = memalign( sysconf( _SC_PAGESIZE), uiMinSize)) == NULL)
{
rc = f_mapPlatformError( errno, NE_FLM_MEM);
goto Exit;
}
#elif defined( FLM_LINUX)
if( posix_memalign( ppvAlloc, sysconf( _SC_PAGESIZE), uiMinSize) != 0)
{
rc = f_mapPlatformError( errno, NE_FLM_MEM);
goto Exit;
}
#elif defined( FLM_UNIX)
{
FLMUINT uiAllocSize;
FLMUINT uiPageSize = (FLMUINT)sysconf( _SC_PAGESIZE);
FLMBYTE * pucAlloc;
uiAllocSize = f_roundUp( uiMinSize, uiPageSize) + uiPageSize;
if( (pucAlloc = (FLMBYTE *)mmap( 0, uiAllocSize,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0)) == MAP_FAILED)
{
rc = f_mapPlatformError( errno, NE_FLM_MEM);
goto Exit;
}
f_assert( ((FLMUINT)(pucAlloc) % uiPageSize) == 0);
UD2FBA( uiAllocSize, pucAlloc);
*ppvAlloc = (void *)(pucAlloc + uiPageSize);
}
#else
if( RC_BAD( rc = f_alloc( uiMinSize, ppvAlloc)))
{
goto Exit;
}
#endif
f_memset( *ppvAlloc, 0, uiMinSize);
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI f_freeAlignedBufferImp(
void ** ppvAlloc)
{
if( *ppvAlloc)
{
#ifdef FLM_WIN
(void)VirtualFree( *ppvAlloc, 0, MEM_RELEASE);
*ppvAlloc = NULL;
#elif defined( FLM_LINUX) || defined( FLM_SOLARIS)
free( *ppvAlloc);
*ppvAlloc = NULL;
#elif defined( FLM_UNIX)
{
FLMUINT uiAllocSize;
FLMUINT uiPageSize = (FLMUINT)sysconf( _SC_PAGESIZE);
*ppvAlloc = ((FLMBYTE *)(*ppvAlloc)) - uiPageSize;
uiAllocSize = FB2UD( (FLMBYTE *)(*ppvAlloc));
munmap( *ppvAlloc, uiAllocSize);
}
*ppvAlloc = NULL;
#else
f_free( ppvAlloc);
#endif
}
}
/****************************************************************************
Desc:
****************************************************************************/
FLMBOOL FLMAPI f_canGetMemoryInfo( void)
{
if( RC_OK( f_getMemoryInfo( NULL, NULL)))
{
return( TRUE);
}
return( FALSE);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_getMemoryInfo(
FLMUINT64 * pui64TotalPhysMem,
FLMUINT64 * pui64AvailPhysMem)
{
RCODE rc = NE_FLM_OK;
FLMUINT64 ui64TotalPhysMem = 0;
FLMUINT64 ui64AvailPhysMem = 0;
#ifdef FLM_WIN
{
MEMORYSTATUS MemStatus;
GlobalMemoryStatus( &MemStatus);
ui64TotalPhysMem = MemStatus.dwTotalPhys;
ui64AvailPhysMem = MemStatus.dwAvailPhys;
// There could be more physical memory in the system than we could
// actually allocate in our virtual address space. Thus, we need to
// make sure that we never exceed our total virtual address space.
if (ui64TotalPhysMem > (FLMUINT)MemStatus.dwTotalVirtual)
{
ui64TotalPhysMem = (FLMUINT)MemStatus.dwTotalVirtual;
}
if( ui64AvailPhysMem > ui64TotalPhysMem)
{
ui64AvailPhysMem = ui64TotalPhysMem;
}
}
#elif defined( FLM_UNIX)
{
FLMUINT uiProcMemLimit = FLM_MAX_UINT;
FLMUINT uiProcVMemLimit = FLM_MAX_UINT;
#if defined( RLIMIT_VMEM)
{
struct rlimit rlim;
// Bump the process soft virtual limit up to the hard limit
if( getrlimit( RLIMIT_VMEM, &rlim) != 0)
{
rlim.rlim_cur = RLIM_INFINITY;
rlim.rlim_max = RLIM_INFINITY;
}
if( rlim.rlim_cur != RLIM_INFINITY)
{
uiProcVMemLimit = (FLMUINT)rlim.rlim_cur;
}
}
#endif
#if defined( RLIMIT_DATA)
{
struct rlimit rlim;
// Bump the process soft heap limit up to the hard limit
if( getrlimit( RLIMIT_DATA, &rlim) != 0)
{
rlim.rlim_cur = RLIM_INFINITY;
rlim.rlim_max = RLIM_INFINITY;
}
if( rlim.rlim_cur != RLIM_INFINITY)
{
uiProcMemLimit = (FLMUINT)rlim.rlim_cur;
}
}
#endif
#ifdef FLM_AIX
struct vminfo tmpvminfo;
#ifdef _SC_PAGESIZE
long iPageSize = sysconf(_SC_PAGESIZE);
#else
long iPageSize = 4096;
#endif
if( iPageSize == -1)
{
// If sysconf returned an error, resort to using the default
// page size for the Power architecture.
iPageSize = 4096;
}
ui64TotalPhysMem = HIGH_FLMUINT;
ui64AvailPhysMem = HIGH_FLMUINT;
if( vmgetinfo( &tmpvminfo, VMINFO, sizeof( tmpvminfo)) != -1)
{
ui64TotalPhysMem = tmpvminfo.memsizepgs * iPageSize;
ui64AvailPhysMem = tmpvminfo.numfrb * iPageSize;
}
#elif defined( FLM_LINUX)
f_getLinuxMemInfo( &ui64TotalPhysMem, &ui64AvailPhysMem);
#elif defined( _SC_PAGESIZE) && defined( _SC_AVPHYS_PAGES)
long iPageSize = sysconf( _SC_PAGESIZE);
// Get the amount of memory available to the system
ui64TotalPhysMem = sysconf( _SC_PHYS_PAGES) * iPageSize;
ui64AvailPhysMem = sysconf( _SC_AVPHYS_PAGES) * iPageSize;
#else
return( RC_SET( NE_FLM_NOT_IMPLEMENTED));
#endif
// The process might be limited in the amount of memory it
// can access.
if( ui64TotalPhysMem > uiProcMemLimit)
{
ui64TotalPhysMem = uiProcMemLimit;
}
if( ui64TotalPhysMem > uiProcVMemLimit)
{
ui64TotalPhysMem = uiProcVMemLimit;
}
}
#elif defined( FLM_LIBC_NLM)
{
#ifndef _SC_PHYS_PAGES
#define _SC_PHYS_PAGES 56
#endif
#ifndef _SCAVPHYS_PAGES
#define _SC_AVPHYS_PAGES 57
#endif
long iPageSize = sysconf( _SC_PAGESIZE);
// Get the amount of memory available to the system
ui64TotalPhysMem = sysconf(_SC_PHYS_PAGES) * iPageSize;
ui64AvailPhysMem = sysconf(_SC_AVPHYS_PAGES) * iPageSize;
}
#elif defined( FLM_RING_ZERO_NLM)
{
FLMUINT uiCacheBufferSize = GetCacheBufferSize();
ui64TotalPhysMem = GetOriginalNumberOfCacheBuffers() * uiCacheBufferSize;
ui64AvailPhysMem = GetCurrentNumberOfCacheBuffers() * uiCacheBufferSize;
// Get available memory in local process pool
{
FLMUINT uiFreeBytes;
FLMUINT uiFreeNodes;
FLMUINT uiAllocatedBytes;
FLMUINT uiAllocatedNodes;
FLMUINT uiTotalMemory;
if (GetNLMAllocMemoryCounts( f_getNLMHandle(),
&uiFreeBytes, &uiFreeNodes,
&uiAllocatedBytes, &uiAllocatedNodes,
&uiTotalMemory) == 0)
{
ui64AvailPhysMem += uiFreeBytes;
}
}
}
#else
rc = RC_SET( NE_FLM_NOT_IMPLEMENTED);
#endif
if( ui64AvailPhysMem > ui64TotalPhysMem)
{
ui64AvailPhysMem = ui64TotalPhysMem;
}
if( pui64TotalPhysMem)
{
*pui64TotalPhysMem = ui64TotalPhysMem;
}
if( pui64AvailPhysMem)
{
*pui64AvailPhysMem = ui64AvailPhysMem;
}
return( rc);
}
/***************************************************************************
Desc:
***************************************************************************/
#ifdef FLM_LINUX
FLMUINT64 f_getLinuxMemInfoValue(
char * pszMemInfoBuffer,
const char * pszTag)
{
char * pszTmp;
FLMUINT64 ui64Bytes = 0;
if( (pszTmp = f_strstr( pszMemInfoBuffer, pszTag)) == NULL)
{
return( 0);
}
pszTmp += f_strlen( pszTag);
while( *pszTmp == ASCII_SPACE)
{
pszTmp++;
}
while( *pszTmp >= '0' && *pszTmp <= '9')
{
ui64Bytes *= 10;
ui64Bytes += (FLMUINT)(*pszTmp - '0');
pszTmp++;
}
return( ui64Bytes * 1024);
}
#endif
/***************************************************************************
Desc:
***************************************************************************/
#ifdef FLM_LINUX
void f_getLinuxMemInfo(
FLMUINT64 * pui64TotalMem,
FLMUINT64 * pui64AvailMem)
{
int fd = -1;
int iBytesRead;
int iMemInfoBufSize = 4096;
char * pszMemInfoBuf = NULL;
FLMUINT64 ui64TotalMem = 0;
FLMUINT64 ui64AvailMem = 0;
if( (pszMemInfoBuf = (char *)os_malloc( iMemInfoBufSize)) == NULL)
{
goto Exit;
}
if( (fd = open( "/proc/meminfo", O_RDONLY, 0600)) == -1)
{
goto Exit;
}
if( (iBytesRead = read( fd, pszMemInfoBuf, iMemInfoBufSize - 1)) == -1)
{
goto Exit;
}
pszMemInfoBuf[ iBytesRead] = 0;
if( (ui64TotalMem =
f_getLinuxMemInfoValue( pszMemInfoBuf, "MemTotal:")) != 0)
{
ui64AvailMem =
f_getLinuxMemInfoValue( pszMemInfoBuf, "MemFree:") +
f_getLinuxMemInfoValue( pszMemInfoBuf, "Buffers:") +
f_getLinuxMemInfoValue( pszMemInfoBuf, "Cached:");
}
Exit:
if( pui64TotalMem)
{
*pui64TotalMem = ui64TotalMem;
}
if( pui64AvailMem)
{
*pui64AvailMem = ui64AvailMem;
}
if( pszMemInfoBuf)
{
os_free( pszMemInfoBuf);
}
if( fd != -1)
{
close( fd);
}
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_RING_ZERO_NLM
void * nlm_realloc(
void * pMemory,
size_t newSize)
{
void * pNewMemory;
LONG lSize;
if( !pMemory)
{
pNewMemory = Alloc( newSize, gv_lAllocRTag);
goto Exit;
}
lSize = SizeOfAllocBlock( pMemory);
pNewMemory = os_malloc( newSize);
if( !pNewMemory)
{
goto Exit;
}
if( lSize > newSize)
{
lSize = newSize;
}
f_memcpy( pNewMemory, pMemory, lSize);
if( pMemory)
{
Free( pMemory);
}
Exit:
return( pNewMemory);
}
#endif
/****************************************************************************
The NetWare Internal Debugger encrypts all symbols by XORing each character
in the symbol name with the character at the corresponding position in the
following mask. To see how the crypt mask is defined in NetWare, see
SYMDEB.386 in the NetWare source. We have three options:
1. We can just emulate the internal debugger and decrypt the symbol,
print it, and reencrypt it each time we want to display it (less than
efficient).
2. We can make a decrypted copy of the symbols we are interested in on
module init, then use our own copy and free it on module exit.
3. We can use the symbol list, but decrypt character by character into
an internal string buffer, then print the buffer.
****************************************************************************/
/****************************************************************************
Desc:
****************************************************************************/
F_ObjRefTracker::F_ObjRefTracker(void)
{
m_hRefListMutex = F_MUTEX_NULL;
m_pListManager = NULL;;
m_pFileSystem = NULL;
m_pszObjName[ 0] = '\0';
m_pAddrFmtHook = NULL;
m_pUserData = NULL;
m_pModHandle = NULL;
}
/****************************************************************************
Desc: Allocates required memory and initializes a local file system
****************************************************************************/
RCODE F_ObjRefTracker::setup(
const char * pszObjName,
FLMBOOL bLogToFile)
{
RCODE rc = NE_FLM_OK;
char pszTmpBuf[ FORTRACK_MAX_OBJ_NAME_LEN + 5];
char * pucTmp;
// Allocate a mutex
if( RC_BAD( rc = f_mutexCreate( &m_hRefListMutex)))
{
goto Exit;
}
// Allocate the list
if( (m_pListManager = f_new F_ListManager( &m_lnode, 1)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
// Create a local file system object
if( bLogToFile)
{
if( RC_BAD( rc = FlmGetFileSystem( &m_pFileSystem)))
{
goto Exit;
}
}
if( f_strlen( pszObjName) <= FORTRACK_MAX_OBJ_NAME_LEN)
{
f_strcpy( m_pszObjName, pszObjName);
}
else
{
f_sprintf( m_pszObjName, "OBJTRCK");
}
// Set the log path
f_strcpy( pszTmpBuf, m_pszObjName);
pucTmp = pszTmpBuf;
while( *pucTmp)
{
if( *pucTmp >= 'a' && *pucTmp <= 'z')
{
*pucTmp = (*pucTmp - 'a') + 'A';
}
pucTmp++;
}
f_strcat( pszTmpBuf, ".OTL");
#ifdef FLM_NLM
f_strcpy( m_pLogPath, "SYS:\\SYSTEM\\");
f_strcat( m_pLogPath, pszTmpBuf);
#else
f_strcpy( m_pLogPath, pszTmpBuf);
#endif
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
F_ObjRefTracker::~F_ObjRefTracker(void)
{
if( m_pListManager)
{
m_pListManager->Release();
m_pListManager = NULL;
}
if( m_hRefListMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &m_hRefListMutex);
}
if( m_pFileSystem)
{
m_pFileSystem->Release();
}
}
/****************************************************************************
Desc: Track the given reference.
****************************************************************************/
void F_ObjRefTracker::trackRef(
void * pReferenceID,
void * pSubrefID)
{
F_TrackingRecord * pTrackingRec = NULL;
void ** pStack;
if( m_hRefListMutex == F_MUTEX_NULL)
{
// Reference Tracking has not been initialized, just exit.
goto Exit;
}
if( !pReferenceID)
{
// Do not track NULL references
goto Exit;
}
// If there is insufficient memory to allocate a tracking record,
// then we will never know if this reference is properly released.
if( (pTrackingRec = f_new F_TrackingRecord(
pReferenceID, pSubrefID)) == NULL)
{
char pucMessage[ 100];
logError( "trackRef: Insufficient memory to allocate tracking record");
f_sprintf( pucMessage, "\treference %x.%x will not be tracked",
(unsigned)((FLMUINT)pReferenceID), (unsigned)((FLMUINT)pSubrefID));
logError( pucMessage);
goto Exit;
}
pTrackingRec->setup( m_pListManager, &m_lnode, 1);
// Add the tracking record to the list
f_mutexLock( m_hRefListMutex);
m_pListManager->insertLast( 0, pTrackingRec);
f_mutexUnlock( m_hRefListMutex);
pStack = (void **)pTrackingRec->getStack();
getCallStack( pStack, CTRC_STACK_SIZE, 1);
Exit:
return;
}
/****************************************************************************
Desc: This reference has been released, don't track it any more.
****************************************************************************/
void F_ObjRefTracker::untrackRef(
void * pReferenceID,
void * pSubrefID)
{
F_TrackingRecord * pTrackingRec = NULL;
FLMBOOL bListLocked = FALSE;
if( m_hRefListMutex == F_MUTEX_NULL)
{
goto Exit;
}
if( !pReferenceID)
{
goto Exit;
}
// Lock the list
f_mutexLock( m_hRefListMutex);
bListLocked = TRUE;
// Try to find the reference in the list
pTrackingRec = (F_TrackingRecord *) m_pListManager->getItem( 0, 0);
while( pTrackingRec)
{
if( pTrackingRec->getReferenceID() == pReferenceID
&& pTrackingRec->getSubrefID() == pSubrefID)
{
// The reference has been found.
pTrackingRec->removeFromList();
pTrackingRec->Release();
break;
}
pTrackingRec = (F_TrackingRecord *) pTrackingRec->getNextListItem();
}
if( !pTrackingRec)
{
// The reference was never tracked. This isn't supposed to happen.
char pucMessage[100];
f_sprintf( pucMessage,
"untrackRef: Reference %x.%x was not tracked",
(unsigned)((FLMUINT)pReferenceID), (unsigned)((FLMUINT)pSubrefID));
logError( pucMessage);
logError( "\tModify code to track this reference");
goto Exit;
}
Exit:
if( bListLocked)
{
f_mutexUnlock( m_hRefListMutex);
}
}
/****************************************************************************
Desc: Check the list for references that were never released.
****************************************************************************/
void F_ObjRefTracker::checkForUnreleasedRefs(
FLMUINT * puiCount)
{
RCODE rc = NE_FLM_OK;
F_TrackingRecord * pTrackingRec;
FLMUINT64 ui64FileCursor;
FLMUINT uiLoop;
char pucSymbol[ 125];
char pucBuffer[ 150];
FLMBOOL bHeaderDisplayed;
IF_FileHdl * pFileHdl = NULL;
FLMBOOL bListLocked = FALSE;
FLMUINT uiCount = 0;
if( m_hRefListMutex == F_MUTEX_NULL)
{
logError( "checkForUnreleasedReferences: Reference tracking "
"was not initialized");
goto Exit;
}
if( m_pFileSystem)
{
if( RC_BAD( rc = m_pFileSystem->openFile( m_pLogPath,
FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYNONE,
&pFileHdl)))
{
if( RC_BAD( rc = m_pFileSystem->createFile( m_pLogPath,
FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYNONE,
&pFileHdl)))
{
goto Exit;
}
}
}
// Find EOF so text can be appended to the trace file.
if( pFileHdl)
{
if( RC_BAD( rc = pFileHdl->size( &ui64FileCursor)))
{
goto Exit;
}
}
// Lock the list
f_mutexLock( m_hRefListMutex);
bListLocked = TRUE;
bHeaderDisplayed = FALSE;
// Process all unreleased references
for( pTrackingRec = (F_TrackingRecord *)m_pListManager->getItem( 0, 0);
pTrackingRec;
pTrackingRec = (F_TrackingRecord *)m_pListManager->getItem( 0, 0))
{
void ** pStack;
uiCount++;
if( !bHeaderDisplayed)
{
f_sprintf( pucBuffer, "Unreleased references of type [%s]\n",
m_pszObjName);
if( RC_BAD( rc = logMessage( pucBuffer, pFileHdl, &ui64FileCursor)))
{
goto Exit;
}
bHeaderDisplayed = TRUE;
}
if( RC_BAD( rc = logMessage( " ", pFileHdl, &ui64FileCursor)))
{
goto Exit;
}
f_sprintf( pucBuffer, " Unreleased reference (%X.%X) from thread: %X\n",
(unsigned)((FLMUINT)pTrackingRec->getReferenceID()),
(unsigned)((FLMUINT)pTrackingRec->getSubrefID()),
(unsigned) pTrackingRec->getThreadID());
if( RC_BAD( rc = logMessage( pucBuffer, pFileHdl, &ui64FileCursor)))
{
goto Exit;
}
pStack = (void **) pTrackingRec->getStack();
for( uiLoop = 0; pStack[ uiLoop]; uiLoop++ )
{
formatAddress( pucSymbol, sizeof( pucSymbol), pStack[ uiLoop]);
f_sprintf( pucBuffer, " %-45.45s [addr = %8.8x]\n", pucSymbol,
(unsigned)((FLMUINT)pStack[ uiLoop]));
if( RC_BAD( rc = logMessage( pucBuffer, pFileHdl, &ui64FileCursor)))
{
goto Exit;
}
}
m_pListManager->removeItem( 0, pTrackingRec);
}
Exit:
if( bListLocked)
{
f_mutexUnlock( m_hRefListMutex);
}
if( pFileHdl)
{
pFileHdl->Release();
}
if( puiCount)
{
*puiCount = uiCount;
}
}
/****************************************************************************
Desc: Sets the address coonversion callback function
****************************************************************************/
void F_ObjRefTracker::setAddressFormatter(
ADDR_FMT_HOOK pFunc,
void * pvUserData)
{
m_pAddrFmtHook = pFunc;
m_pUserData = pvUserData;
}
/****************************************************************************
Desc: Converts a return address to displayable format
****************************************************************************/
void F_ObjRefTracker::formatAddress(
char * pucBuffer,
FLMUINT uiSize,
void * pAddress)
{
#ifdef FLM_WIN
PIMAGEHLP_SYMBOL pihs = NULL;
#ifdef FLM_64BIT
DWORD64 displacement;
#else
DWORD displacement;
#endif
RCODE rc = NE_FLM_OK;
#endif
if( m_pAddrFmtHook)
{
pucBuffer[ 0] = '\0';
m_pAddrFmtHook( this, pAddress, (FLMBYTE *)pucBuffer, uiSize, m_pUserData);
return;
}
#if defined( FLM_RING_ZERO_NLM)
if( uiSize == 0)
{
return;
}
GetClosestSymbol( (BYTE *)pucBuffer, (LONG)pAddress);
return;
#elif defined( FLM_WIN)
if( RC_OK( rc = f_alloc( sizeof( IMAGEHLP_SYMBOL) + 100, &pihs)))
{
pihs->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
pihs->Address = (FLMUINT)pAddress; //stackFrame.AddrPC.Offset;
pihs->MaxNameLength = (FLMINT32)uiSize;
if ( SymGetSymFromAddr( GetCurrentProcess(), (FLMUINT)pAddress,
&displacement, pihs ) )
{
wsprintf( pucBuffer, "%s + %X",
(char *)pihs->Name, (unsigned)displacement);
}
else
{
wsprintf( pucBuffer, "0x%08X", (unsigned)((FLMUINT)pAddress));
}
}
else
{
wsprintf( pucBuffer, "0x%08X", (unsigned)((FLMUINT)pAddress));
}
f_free( &pihs);
#else
#ifdef HAVE_DLADDR
Dl_info dlip;
if (dladdr(pAddress, &dlip) != 0 && dlip.dli_sname)
{
const char *filename;
if (dlip.dli_saddr != pAddress)
{
filename = strrchr(dlip.dli_fname, '/');
if (!filename)
filename = dlip.dli_fname;
else
filename++; // skip over slash
f_sprintf( pucBuffer, "0x%08x (%s)", (unsigned)((FLMUINT)pAddress),
filename);
}
else
f_sprintf( pucBuffer, "%s", dlip.dli_sname);
return;
}
#endif
f_sprintf( pucBuffer, "0x%08x", (unsigned)((FLMUINT)pAddress));
#endif
}
/****************************************************************************
Desc: Walk the BP chain down the call stack, gathering return addresses.
****************************************************************************/
#if defined( FLM_WIN) && !defined( FLM_64BIT)
void F_ObjRefTracker::getCallStack(
void * stack[],
FLMUINT uiCount,
FLMUINT uiSkip)
{
STACKFRAME stackFrame;
FLMINT32 ui32LastBP;
FLMUINT uiLoop;
F_UNREFERENCED_PARM( uiSkip);
ZeroMemory( (PVOID)&stackFrame, sizeof(STACKFRAME) );
// TDOMAN: do this in assembly since we aren't sure we can rely on the
// GetThreadContext and StackWalk API's
_asm
{
mov ui32LastBP, ebp // save off next bp
}
// while you can continue walking the stack...
for ( uiLoop = 0; uiLoop < uiCount; uiLoop++ )
{
// TDOMAN: we have to walk the stack ourselves since the VC4x API's
// don't appear to be consistently reliable.
__try
{
// TDOMAN: don't crash if the last bp wasn't want we expected
_asm
{
push esi
push edi
mov edi, ui32LastBP
mov esi, [edi]
cmp esi, edi
jbe Done
mov ui32LastBP, esi // save off next bp
mov esi, [edi + 4]
mov stackFrame, esi // setup AddrPC
pop edi
pop esi
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// If you do want to get these exceptions, you can turn off stack
// walking by setting fStackWalk to FALSE.
goto Done;
}
stack[ uiLoop] = (void *)(FLMUINT)stackFrame.AddrPC.Offset;
}
Done:
stack[ uiLoop] = (void *)0;
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_64BIT)
void F_ObjRefTracker::getCallStack(
void * stack[],
FLMUINT, //uiCount,
FLMUINT) //uiSkip)
{
// Not supported on this platform
stack[0] = (void *)0;
}
#endif
/****************************************************************************
****************************************************************************/
#if defined( FLM_NLM)
void * DMGetEBP(void);
#if defined( __MWERKS__)
void *DMGetEBP(void)
{
__asm
{
mov eax,[ebp]
}
} // end of assembly
#else
#pragma aux DMGetEBP = "mov eax,ebp";
#endif
/****************************************************************************
Desc:
****************************************************************************/
void * DMValueAtStackOffset(void *pos, int offset);
#if defined( __MWERKS__)
void *DMValueAtStackOffset(void *, int )
{
__asm
{
mov eax,[ebp+0x8]
mov ebx,[ebp+0xC]
mov eax,ss:[eax+ebx]
}
}
#else
#pragma aux DMValueAtStackOffset = "mov eax,ss:[eax+ebx]" parm [eax] [ebx];
#endif
/****************************************************************************
Desc: Traces back COUNT entries on the call stack, storing
them in STACK. Note that this code requires that DS be build using the
Watcom /of+ option, (or equivalent) to generate traceable stack frames by
emitting prelude code for every function that looks something like that
of the SubRoutine below:
Caller:
push Parms ; caller pushes parameters
call SubRoutine ; caller pushes his own return address
add esp, parmSize ; caller clears parameters from stack
...
SubRoutine:
push ebp ; pushes caller's frame pointer
mov ebp, esp ; creates SubRoutine's frame pointer
...
pop ebp ; restores caller's frame pointer
ret ; returns to caller
In this scheme, the MOV instruction in the prelude code always sets EBP
pointing to the PUSHed value of the previous (caller's) frame pointer
(see the first instruction in SubRoutine above). We discard the first
'skipCount' + 1 return addresses because we aren't interested in the fact
that the caller called DMAlloc, which called DMAllocFromTag,
which called getCallStack.
The stack trace loop terminates when it detects a return address that is
outside of NDS code space (start and limit are stored in the load def
struct - module handle). Some inline assembly is used to access the stack
like data (see DMGetEBP and DMValueAtStackOffset above).
****************************************************************************/
void F_ObjRefTracker::getCallStack(
void * stack[],
FLMUINT uiCount,
FLMUINT uiSkipCount)
{
FLMUINT uiLoop;
void * rtnAddr;
void * ebp = DMGetEBP();
while( uiSkipCount--)
{
ebp = DMValueAtStackOffset( ebp, 0);
}
rtnAddr = DMValueAtStackOffset( ebp, 4);
for( uiLoop = 0; --uiCount; )
{
void *oldebp;
stack[ uiLoop++] = rtnAddr;
if( !ebp)
{
break;
}
oldebp = ebp;
ebp = DMValueAtStackOffset( ebp, 0); // Caller's frame ptr
if ( !ebp || ebp <= oldebp || ebp > (void *)((char *)oldebp+3000))
{
break;
}
rtnAddr = DMValueAtStackOffset( ebp, 4); // Caller's return addr
}
stack[ uiLoop] = 0;
return;
}
#endif // defined( FLM_NLM)
/****************************************************************************
Desc: Log an error message
****************************************************************************/
void F_ObjRefTracker::logError(
const char * pucMessage)
{
char pucBuffer[ 120];
FLMUINT64 ui64Dummy = 0;
f_sprintf( pucBuffer, "Error: %s", pucMessage);
logMessage( pucBuffer, NULL, &ui64Dummy);
flmAssert(0);
}
/****************************************************************************
Desc: Log a message to the trace file and to the DS trace screen
****************************************************************************/
RCODE F_ObjRefTracker::logMessage(
const char * message,
IF_FileHdl * pFileHdl,
FLMUINT64 * pui64FileCursor)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiBytesWritten;
FLMBOOL bFileOpened = FALSE;
const char * pCarriageReturn = "\n";
if( !pFileHdl && m_pFileSystem)
{
if( RC_BAD( rc = m_pFileSystem->openFile(
m_pLogPath, FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pFileHdl)))
{
if( RC_BAD( rc = m_pFileSystem->createFile(
m_pLogPath, FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYNONE,
&pFileHdl)))
{
goto Exit;
}
}
bFileOpened = TRUE;
flmAssert( pFileHdl);
// Find EOF so text can be appended to the trace file.
if( RC_BAD( rc = pFileHdl->size( pui64FileCursor)))
{
goto Exit;
}
}
if( pFileHdl)
{
if( RC_BAD( rc = pFileHdl->write(
*pui64FileCursor, f_strlen( message), (void *)message,
&uiBytesWritten)))
{
goto Exit;
}
(*pui64FileCursor) += uiBytesWritten;
if( RC_BAD( rc = pFileHdl->write(
*pui64FileCursor, f_strlen( pCarriageReturn),
(void *)pCarriageReturn, &uiBytesWritten)))
{
(*pui64FileCursor) += uiBytesWritten;
}
}
Exit:
if( bFileOpened)
{
pFileHdl->Release();
}
return( rc);
}
#undef new
#undef delete
/****************************************************************************
Desc:
****************************************************************************/
void * F_Object::operator new(
FLMSIZET uiSize,
const char * pszFile,
int iLine)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
void * pvReturnPtr = NULL;
f_allocImp( uiSize, &pvReturnPtr, TRUE, pszFile, iLine);
return( pvReturnPtr);
}
/****************************************************************************
Desc:
****************************************************************************/
void * F_Object::operator new(
FLMSIZET uiSize)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
void * pvReturnPtr = NULL;
f_allocImp( uiSize, &pvReturnPtr, TRUE, NULL, 0);
return( pvReturnPtr);
}
/****************************************************************************
Desc:
****************************************************************************/
void * F_Object::operator new[](
FLMSIZET uiSize,
const char * pszFile,
int iLine)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
void * pvReturnPtr = NULL;
f_allocImp( uiSize, &pvReturnPtr, TRUE, pszFile, iLine);
return( pvReturnPtr);
}
/****************************************************************************
Desc:
****************************************************************************/
void * F_Object::operator new[](
FLMSIZET uiSize)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
void * pvReturnPtr = NULL;
f_allocImp( uiSize, &pvReturnPtr, TRUE, NULL, 0);
return( pvReturnPtr);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_Object::operator delete(
void * ptr)
{
if( !ptr)
{
return;
}
f_freeImp( &ptr, TRUE);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_Object::operator delete[](
void * ptr)
{
if( !ptr)
{
return;
}
f_freeImp( &ptr, TRUE);
}
/****************************************************************************
Desc:
****************************************************************************/
#if !defined( FLM_WATCOM_NLM)
void F_Object::operator delete(
void * ptr,
const char *, // file
int) // line
{
if( !ptr)
{
return;
}
f_freeImp( &ptr, TRUE);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if !defined( FLM_WATCOM_NLM)
void F_Object::operator delete[](
void * ptr,
const char *, // file
int) // line
{
if( !ptr)
{
return;
}
f_freeImp( &ptr, TRUE);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
void * F_OSBase::operator new(
FLMSIZET uiSize,
const char *, // pszFile,
int) // iLine)
#ifndef FLM_WATCOM_NLM
throw()
#endif
{
return( os_malloc( uiSize));
}
/************************************************************************
Desc:
*************************************************************************/
void F_OSBase::operator delete(
void * ptr)
{
os_free( ptr);
}
/************************************************************************
Desc:
*************************************************************************/
void F_OSBase::operator delete[](
void * ptr)
{
os_free( ptr);
}
/****************************************************************************
Desc:
****************************************************************************/
#if !defined( FLM_WATCOM_NLM)
void F_OSBase::operator delete(
void * ptr,
const char *, // file
int) // line
{
os_free( ptr);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if !defined( FLM_WATCOM_NLM)
void F_OSBase::operator delete[](
void * ptr,
const char *, // file
int) // line
{
os_free( ptr);
}
#endif