git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@675 0109f412-320b-0410-ab79-c3e0c5ffbbe6
6170 lines
134 KiB
C++
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
|