/*============================================================================= MEMORY.H: Definitions for Homeworld memory management module. Created June 1997 by Luke Moloney =============================================================================*/ #ifndef ___MEMORY_H #define ___MEMORY_H #include "task.h" #include "linkedlist.h" /*============================================================================= Switches: =============================================================================*/ #define MEM_FORCE_CREEP_UP 0 //force the memory pointers to creep up #define MEM_SMALL_BLOCK_HEAP 1 //enable small block heap allocations #define MEM_VOLATILE_CLEARING 1 //the clear dwords are volatile and unreliable #ifndef HW_Release #define MEM_MODULE_TEST 0 //test the module #define MEM_ERROR_CHECKING 1 //basic error checking #define MEM_CLEAR_MEM 1 //clear newly allocated blocks to all clear code #define MEM_CLEAR_MEM_ON_FREE 1 //clear newly freed blocks to all clear code #define MEM_VERBOSE_LEVEL 1 //control verbose printing #define MEM_USE_NAMES 1 //use names in allocated blocks #define MEM_DEFRAGMENT_FREE 1 //attempt to join with adjacent free block(s) when freeing #define MEM_ANALYSIS 1 //enable memory analysis functions #define MEM_STATISTICS 1 //track memory usage stats #define MEM_ANALYSIS_KEY QKEY //create a memory analysis on demand #define MEM_DETECT_VOLATILE 1 //automatically detect what memory blocks are volatile #define MEM_FILE_NV 0 //log non-volatile allocs to a file #define MEM_DEBUG_NV 0 //log non-volatile allocs to debug window #define MEM_SMALLBLOCK_STATS 1 //log usage stats on small memory blocks #define MEM_LOG_MOSTVOLATILE 1 //keep track of the most volatile types of memory #define MEM_ANALYSIS_AUTOCREATE 1 //automatically create memory analysis if there's an allocation failure #define MEM_VOLATILE_CLEARING 1 //the clear dwords are volatile and unreliable #define MEM_ANAL_CHECKING 1 //extra hard-core debuggery action #else //HW_Debug #define MEM_MODULE_TEST 0 //don't test the module #define MEM_ERROR_CHECKING 0 //no error ckecking in retail #define MEM_CLEAR_MEM 0 //don't clear any blocks to zeros #define MEM_CLEAR_MEM_ON_FREE 0 //clear newly freed blocks to all clear code #define MEM_VERBOSE_LEVEL 0 //don't print any verbose strings in retail #define MEM_USE_NAMES 0 //no names please ma'am #define MEM_DEFRAGMENT_FREE 1 //attempt to join with adjacent free block(s) when freeing #define MEM_ANALYSIS 0 //disable memory analysis functions #define MEM_STATISTICS 0 //track memory usage stats #define MEM_ANALYSIS_KEY 0 //create a memory analysis on demand #define MEM_DETECT_VOLATILE 0 //automatically detect what memory blocks are volatile #define MEM_FILE_NV 0 //log non-volatil allocs to a file #define MEM_DEBUG_NV 0 //log non-volatil allocs to debug window #define MEM_SMALLBLOCK_STATS 0 //log usage stats on small memory blocks #define MEM_LOG_MOSTVOLATILE 0 //keep track of the most volatile types of memory #define MEM_ANALYSIS_AUTOCREATE 0 //automatically create memory analysis if there's an allocation failure #define MEM_ANAL_CHECKING 0 //extra hard-core debuggery action #endif //HW_Debug /*============================================================================= Definitions: =============================================================================*/ //memory block flags #define MBF_AllocatedNext 1 //memory following block allocated #define MBF_AllocatedPrev 2 //preceeding memory block allocated //#define MBF_Purgable 4 //will be purged on next call to memPurge() #define MBF_SmallBlockHeap 4 //allocated from a small block heap #define Flammable MBF_SmallBlockHeap//another way of saying very volatile #define Pyrophoric MBF_SmallBlockHeap//another way of saying very volatile #define MBF_Volatile 0 //expect this block to be freed soon #define Volatile MBF_Volatile #define MBF_NonVolatile 8 #define MBF_String 16 #define NonVolatile MBF_NonVolatile #define MBF_ExtendedPoolOnly 32 #define ExtendedPool MBF_ExtendedPoolOnly #define MBF_VerifyMask 0xffffffc0 //remaining bits used for verification mask #define MBF_ParameterMask (MBF_NonVolatile | MBF_SmallBlockHeap | MBF_String) //which parameter flags to store in the cookie flags //verification value #define MBF_VerifyValue (0xfa7babe5 & MBF_VerifyMask) //max length for cookie name strings #if MEM_DETECT_VOLATILE #define MEM_NameLength 16 #else #define MEM_NameLength 20 #endif #if MEM_LOG_MOSTVOLATILE #define MBH_NameLength 4 #else #define MBH_NameLength 8 #endif //allocation block granularity. #define MEM_BlockSize ((sdword)sizeof(memcookie)) #define MEM_BlocksPerCookie 1 //default memory heap size #define MEM_HeapSizeDefault (18 * 1024 * 1024) #define MEM_HeapDefaultScalar 0.40f #define MEM_HeapDefaultMax (64 * 1024 * 1024) //default name strings #define MEM_NameHeap "HeapLow(free)" #define MEM_NameHeapFirst "HeapLowFirst(Free)" #define MEM_HeapFree "(free)" #define MEM_NameHeapLast "HeapHighFirst(free)" //memory clearing settings #define MEM_ClearSetting 0x4e7a110c #define MEM_FreeSetting 0xf4eeda7a //memory statistics #define MEM_AllocsPerStatPrint 100 #define MEM_StatsKey QKEY //small block allocation defines //the smallest small allocation block must be smaller than the smallest allocation #define MSB_SizeMin MEM_BlockSize #define MSB_SizeMax 2048 #define MSB_NumberSizes ((MSB_SizeMax - MSB_SizeMin) / MEM_BlockSize) #define MEM_OptCounterFreqSmall 2048 #define MEM_OptCounterFreqBig 8192 #define MEM_GrowFactor 96 / 256 //grow by 37.5% when a SBH pool runs out //for printing memory statistics #define MEM_TaskStatsPeriod 1.0 //once per second #define MEM_VolatileLong 5.0f //any block that is freed after this amount of time is non-volatile #define MEM_VolatileShort 1.0f #define MEM_HeapValidation 0xbad600f //bad guy validation //byte offsets for finding a heap from the linked list #define MBH_AllocatedLinkOffset 0 //#define MBH_FreeLinkOffset (4 + sizeof(LinkedList)) //definitions for cookie volatility logging #define MEM_NumberVolatileStats 768 //definitions for tracking the most commonly allocated cookie names #define MS_NumberCookieNames 64 #define MS_OutputStringLength 128 #define MS_NameStringLength 64 //definitions for growing the memory heaps #define MGH_NumberGrowthHeaps 16 //maximum number of extra heaps we can grow to #define MGH_MinGrowthSize (4 * 1024 * 1024 - sizeof(memcookie) * 2)//grow in 4MB increments. That's 64MB extra heap size. Should be sufficient. /*============================================================================= Type definitions: =============================================================================*/ //structure for a memory block cookie typedef struct { udword flags; //validation and information #if MEM_USE_NAMES char name[MEM_NameLength]; //name of memory block #else ubyte pad[MEM_NameLength]; //round structure up to 16 bytes #endif sdword blocksNext; //number of blocks to next cookie sdword blocksPrevious; //number of blocks to previous cookie //only valid when freeing memory #if MEM_DETECT_VOLATILE real32 timeAllocated; //when was this block allocated? #endif } memcookie; //structure for a cookie in a small block heap. These cookies are created only once. //location of flags and name string must be compatable with the regular cookies because //some functions are shared between cookie structure formats. #if MEM_SMALL_BLOCK_HEAP typedef struct { udword flags; //validation and information #if MEM_USE_NAMES char name[MBH_NameLength]; //name of memory block (must correspond to location in structure and size as the name in regular cookies) #else char pad[MBH_NameLength]; #endif Node link; //tied to whatever linked list it's in sdword length; //un-rounded length of block allocated #if MEM_LOG_MOSTVOLATILE real32 timeAllocated; //when was this block allocated? #endif } mbhcookie; //structure for a small block heap typedef struct { LinkedList allocated; LinkedList free; sdword blockSize; sdword nBlocks; mbhcookie *heapStart; mbhcookie *heapEnd; #if MEM_ERROR_CHECKING udword validation; //used to detect that we're in a proper small block heap #endif #if MEM_SMALLBLOCK_STATS sdword nAllocationAttempts; //number of allocations attempted in this heap sdword nOverflowedAllocations; //number of allocations which overflowed this heap real32 usage; //accumulated usage, blocks real32 firstAllocTime; //time this logging began #endif } mbheap; #endif//MEM_SMALL_BLOCK_HEAP #if MEM_LOG_MOSTVOLATILE //structure for logging most volatile type of allocations typedef struct { char name[MEM_NameLength]; //name of this type allocation real32 nLogged; //number of logged allocations real32 totalTime; //total length of allocated real32 volatility; //computed and stored for sorting } volatilestat; #endif #if MEM_STATISTICS //structure for logging most commonly allocated cookie types typedef struct { char name[MS_NameStringLength]; sdword nAllocs; sdword nTotalSize; char outputString[MS_OutputStringLength]; } memcookiename; #endif //structure for a memory heap. This contains all other memory structures typedef struct { sdword heapLength; //size of heap sdword poolLength; //size of pool including small block heaps void *wholePool; //possibly non-aligned heap base ubyte *pool; //base of heap, aligned on a cookie boundary memcookie *first; //first cookie, start of heap memcookie *firstFree; //lowest free cookie memcookie *lastFree; //cookie AFTER last free cookie memcookie *last; //last cookie, end of heap memcookie *biggestBlock; //biggest block in the pool memcookie *biggestBlockSize; //size of that biggest block memcookie *firstSize[MSB_NumberSizes + 1]; //lowest free cookie of sizes memcookie *lastSize[MSB_NumberSizes + 1]; //highest free cookie of sizes } mempool; //info on small block heaps. Pretty simple typedef struct { sdword blockSize; //size of blocks the small block heap sdword nBlocks; //number of blocks of said size } memsmallheapinfo; typedef void *(*memgrowcallback)(sdword heapSize);//callback for growing memory typedef void (memgrowthfreecallback)(void *heap);//callback for freeing growth heaps /*============================================================================= Data: =============================================================================*/ //memory statistics #if MEM_STATISTICS extern sdword memNumberWalks; extern sdword memNumberAllocs; extern char memStatString[256]; extern bool memStatsLogging; extern taskhandle memStatsTaskHandle; extern void memStatsTaskFunction(void); extern memcookiename memStatsCookieNames[MS_NumberCookieNames]; #endif extern mempool memMainPool; /*============================================================================= Macros: =============================================================================*/ //name-specific stuff #if MEM_USE_NAMES #define memAlloc(l, n, f) memAllocFunction((l), (n), (f)); #define memAllocAttempt(l, n, f) memAllocAttemptFunction((l), (n), (f)); #define memNameSet(c, s) memNameSetFunction((c), (s)) #define mbhNameSet(c, s) mbhNameSetFunction((c), (s)) #define memNameSetLong(c, s) memNameSetFunction((c), (s)) #define memRealloc(p, l, n, f) memReallocFunction((p), (l), (n), (f)); #else #define memAlloc(l, n, f) memAllocFunction((l), (f)); #define memAllocAttempt(l, n, f) memAllocAttemptFunction((l), (f)); #define memNameSet(c, s) #define mbhNameSet(c, s) #define memNameSetLong(c, s) #define memRealloc(p, l, n, f) memReallocFunction((p), (l), (f)); #endif//MEM_USE_NAMES //block size macros #define memRoundUp(n) (((n) + (MEM_BlockSize - 1)) & (~(MEM_BlockSize - 1))) #define memRoundDown(n) ((n) & (~(MEM_BlockSize - 1))) #define memBlocksCompute(s, e) ((memRoundDown((udword)(e)) - memRoundUp((udword)(s))) / MEM_BlockSize) #define memBlocksToBytes(b) ((b) * MEM_BlockSize) #define memBytesToBlocks(b) ((b) / MEM_BlockSize) //verify that a cookie is actually a cookie #if MEM_ERROR_CHECKING #define memCookieVerify(c)\ if (((c)->flags & MBF_VerifyMask) != MBF_VerifyValue)\ dbgFatalf(DBG_Loc, "Corrupt cookie: 0x%x", (c)->flags & MBF_VerifyMask) #define mbhCookieVerify(c)\ if (((c)->flags & MBF_VerifyMask) != MBF_VerifyValue)\ dbgFatalf(DBG_Loc, "Corrupt small block heap cookie: 0x%x", (c)->flags & MBF_VerifyMask) #else #define memCookieVerify(c) #define mbhCookieVerify(c) #endif //if module not started properly, generate an error #if MEM_ERROR_CHECKING #define memInitCheck()\ if (memModuleInit == FALSE)\ {\ dbgFatal(DBG_Loc, "Called before memory module started or after closed.");\ } #else #define memInitCheck() #endif //small block allocation #define msbSizeBytes(length) (pool->firstSize[((length) / MEM_BlockSize) - 1]) #define msbSizeBlocks(length) (pool->firstSize[length - 1]) #define msbSizeIndex(length) (((length) / MEM_BlockSize) - 1) #define mslSizeBytes(length) (pool->lastSize[((length) / MEM_BlockSize) - 1]) #define mslSizeBlocks(length) (pool->lastSize[length - 1]) #define mslSizeIndex(length) (((length) / MEM_BlockSize) - 1) #if MEM_LOG_MOSTVOLATILE #define memVolatilityLog(c) memVolatilityLogFuntion(c) #else #define memVolatilityLog(c) #endif /*============================================================================= Functions: =============================================================================*/ //startup/shutdown/reset memory module sdword memStartup(void *heapStart, sdword heapSize, memgrowcallback grow); sdword memReset(void); sdword memClose(memgrowthfreecallback free); //allocate/free memory blocks #if MEM_USE_NAMES void *memAllocFunction(sdword length, char *name, udword flags); #else void *memAllocFunction(sdword length, udword flags); #endif #if MEM_USE_NAMES void *memReallocFunction(void *currentPointer, sdword newSize, char *name, udword flags); void *memAllocAttemptFunction(sdword length, char *name, udword flags); #else void *memReallocFunction(void *currentPointer, sdword newSize, udword flags); void *memAllocAttemptFunction(sdword length, udword flags); #endif void memFree(void *pointer); char *memStringDupe(char *string); char *memStringDupeNV(char *string); //utility functions (many stubbed out in retail builds) #if MEM_ANALYSIS void memAnalysisCreate(void); #endif #if MEM_USE_NAMES sdword memNameSetFunction(memcookie *cookie, char *name); #if MEM_SMALL_BLOCK_HEAP sdword mbhNameSetFunction(mbhcookie *cookie, char *name); #endif sdword memNameSetLongFunction(memcookie *cookie, char *name); void memRename(void *pointer, char *newName); #else #define memRename(p, n) #endif //MEM_USE_NAMES sdword memClearDword(void *dest, udword pattern, sdword nDwords); char *memStrncpy(char *dest, char *source, sdword count); #if MEM_ANALYSIS sdword memFreeMemGet(mempool *pool); #endif #endif //___MEMORY_H