123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959 |
- // Created 2/3/03 by Brian Osman - split Zone code from common.cpp
- #include "../game/q_shared.h"
- #include "qcommon.h"
- #include "../qcommon/sstring.h"
- #include "platform.h"
- #ifdef DEBUG_ZONE_ALLOCS
- int giZoneSnaphotNum=0;
- #define DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE 256
- typedef sstring<DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE> sDebugString_t;
- #endif
- static void Z_Details_f(void);
- // define a string table of all mem tags...
- //
- #ifdef TAGDEF // itu?
- #undef TAGDEF
- #endif
- #define TAGDEF(blah) #blah
- static const char *psTagStrings[TAG_COUNT+1]= // +1 because TAG_COUNT will itself become a string here. Oh well.
- {
- #include "../qcommon/tags.h"
- };
- // This handles zone memory allocation.
- // It is a wrapper around malloc with a tag id and a magic number at the start
- #define ZONE_MAGIC 0x21436587
- // if you change ANYTHING in this structure, be sure to update the tables below using DEF_STATIC...
- //
- typedef struct zoneHeader_s
- {
- int iMagic;
- memtag_t eTag;
- int iSize;
- struct zoneHeader_s *pNext;
- struct zoneHeader_s *pPrev;
- #ifdef DEBUG_ZONE_ALLOCS
- char sSrcFileBaseName[MAX_QPATH];
- int iSrcFileLineNum;
- char sOptionalLabel[DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE];
- int iSnapshotNumber;
- #endif
- } zoneHeader_t;
- typedef struct
- {
- int iMagic;
- } zoneTail_t;
- static inline zoneTail_t *ZoneTailFromHeader(zoneHeader_t *pHeader)
- {
- return (zoneTail_t*) ( (char*)pHeader + sizeof(*pHeader) + pHeader->iSize );
- }
- #ifdef DETAILED_ZONE_DEBUG_CODE
- map <void*,int> mapAllocatedZones;
- #endif
- typedef struct zoneStats_s
- {
- int iCount;
- int iCurrent;
- int iPeak;
- // I'm keeping these updated on the fly, since it's quicker for cache-pool
- // purposes rather than recalculating each time...
- //
- int iSizesPerTag [TAG_COUNT];
- int iCountsPerTag[TAG_COUNT];
- } zoneStats_t;
- typedef struct zone_s
- {
- zoneStats_t Stats;
- zoneHeader_t Header;
- } zone_t;
- cvar_t *com_validateZone;
- zone_t TheZone = {0};
- // Scans through the linked list of mallocs and makes sure no data has been overwritten
- int Z_Validate(void)
- {
- int ret=0;
- if(!com_validateZone || !com_validateZone->integer)
- {
- return ret;
- }
- zoneHeader_t *pMemory = TheZone.Header.pNext;
- while (pMemory)
- {
- #ifdef DETAILED_ZONE_DEBUG_CODE
- // this won't happen here, but wtf?
- int& iAllocCount = mapAllocatedZones[pMemory];
- if (iAllocCount <= 0)
- {
- Com_Error(ERR_FATAL, "Z_Validate(): Bad block allocation count!");
- return ret;
- }
- #endif
- if(pMemory->iMagic != ZONE_MAGIC)
- {
- Com_Error(ERR_FATAL, "Z_Validate(): Corrupt zone header!");
- return ret;
- }
- // this block of code is intended to make sure all of the data is paged in
- if (pMemory->eTag != TAG_IMAGE_T
- && pMemory->eTag != TAG_MODEL_MD3
- && pMemory->eTag != TAG_MODEL_GLM
- && pMemory->eTag != TAG_MODEL_GLA ) //don't bother with disk caches as they've already been hit or will be thrown out next
- {
- unsigned char *memstart = (unsigned char *)pMemory;
- int totalSize = pMemory->iSize;
- while (totalSize > 4096)
- {
- memstart += 4096;
- ret += (int)(*memstart); // this fools the optimizer
- totalSize -= 4096;
- }
- }
- if (ZoneTailFromHeader(pMemory)->iMagic != ZONE_MAGIC)
- {
- Com_Error(ERR_FATAL, "Z_Validate(): Corrupt zone tail!");
- return ret;
- }
-
- pMemory = pMemory->pNext;
- }
- return ret;
- }
-
- // static mem blocks to reduce a lot of small zone overhead
- //
- #pragma pack(push)
- #pragma pack(1)
- typedef struct
- {
- zoneHeader_t Header;
- // byte mem[0];
- zoneTail_t Tail;
- } StaticZeroMem_t;
- typedef struct
- {
- zoneHeader_t Header;
- byte mem[2];
- zoneTail_t Tail;
- } StaticMem_t;
- #pragma pack(pop)
- const static StaticZeroMem_t gZeroMalloc =
- { {ZONE_MAGIC, TAG_STATIC,0,NULL,NULL},{ZONE_MAGIC}};
- #ifdef DEBUG_ZONE_ALLOCS
- #define DEF_STATIC(_char) {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL, "<static>",0,"",0},_char,'\0',{ZONE_MAGIC}
- #else
- #define DEF_STATIC(_char) {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL },_char,'\0',{ZONE_MAGIC}
- #endif
- const static StaticMem_t gEmptyString =
- { DEF_STATIC('\0') };
- const static StaticMem_t gNumberString[] = {
- { DEF_STATIC('0') },
- { DEF_STATIC('1') },
- { DEF_STATIC('2') },
- { DEF_STATIC('3') },
- { DEF_STATIC('4') },
- { DEF_STATIC('5') },
- { DEF_STATIC('6') },
- { DEF_STATIC('7') },
- { DEF_STATIC('8') },
- { DEF_STATIC('9') },
- };
- qboolean gbMemFreeupOccured = qfalse;
- #ifdef DEBUG_ZONE_ALLOCS
- void *_D_Z_Malloc ( int iSize, memtag_t eTag, qboolean bZeroit, const char *psFile, int iLine)
- #else
- void *Z_Malloc(int iSize, memtag_t eTag, qboolean bZeroit, int unusedAlign)
- #endif
- {
- gbMemFreeupOccured = qfalse;
- if (iSize == 0)
- {
- zoneHeader_t *pMemory = (zoneHeader_t *) &gZeroMalloc;
- return &pMemory[1];
- }
- // Add in tracking info and round to a longword... (ignore longword aligning now we're not using contiguous blocks)
- //
- // int iRealSize = (iSize + sizeof(zoneHeader_t) + sizeof(zoneTail_t) + 3) & 0xfffffffc;
- int iRealSize = (iSize + sizeof(zoneHeader_t) + sizeof(zoneTail_t));
- // Allocate a chunk...
- //
- zoneHeader_t *pMemory = NULL;
- while (pMemory == NULL)
- {
- #ifdef _WIN32
- if (gbMemFreeupOccured)
- {
- Sleep(1000); // sleep for a second, so Windows has a chance to shuffle mem to de-swiss-cheese it
- }
- #endif
- if (bZeroit) {
- pMemory = (zoneHeader_t *) calloc ( iRealSize, 1 );
- } else {
- pMemory = (zoneHeader_t *) malloc ( iRealSize );
- }
- if (!pMemory)
- {
- // new bit, if we fail to malloc memory, try dumping some of the cached stuff that's non-vital and try again...
- //
- // ditch the BSP cache...
- //
- if (CM_DeleteCachedMap(qfalse))
- {
- gbMemFreeupOccured = qtrue;
- continue; // we've just ditched a whole load of memory, so try again with the malloc
- }
- // ditch any sounds not used on this level...
- //
- extern qboolean SND_RegisterAudio_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel);
- if (SND_RegisterAudio_LevelLoadEnd(qtrue))
- {
- gbMemFreeupOccured = qtrue;
- continue; // we've dropped at least one sound, so try again with the malloc
- }
- // ditch any image_t's (and associated GL texture mem) not used on this level...
- //
- extern qboolean RE_RegisterImages_LevelLoadEnd(void);
- if (RE_RegisterImages_LevelLoadEnd())
- {
- gbMemFreeupOccured = qtrue;
- continue; // we've dropped at least one image, so try again with the malloc
- }
- // ditch the model-binaries cache... (must be getting desperate here!)
- //
- extern qboolean RE_RegisterModels_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel);
- if (RE_RegisterModels_LevelLoadEnd(qtrue))
- {
- gbMemFreeupOccured = qtrue;
- continue;
- }
- // as a last panic measure, dump all the audio memory, but not if we're in the audio loader
- // (which is annoying, but I'm not sure how to ensure we're not dumping any memory needed by the sound
- // currently being loaded if that was the case)...
- //
- // note that this keeps querying until it's freed up as many bytes as the requested size, but freeing
- // several small blocks might not mean that one larger one is satisfiable after freeup, however that'll
- // just make it go round again and try for freeing up another bunch of blocks until the total is satisfied
- // again (though this will have freed twice the requested amount in that case), so it'll either work
- // eventually or not free up enough and drop through to the final ERR_DROP. No worries...
- //
- extern qboolean gbInsideLoadSound;
- extern int SND_FreeOldestSound(void); // I had to add a void-arg version of this because of link issues, sigh
- if (!gbInsideLoadSound)
- {
- int iBytesFreed = SND_FreeOldestSound();
- if (iBytesFreed)
- {
- int iTheseBytesFreed = 0;
- while ( (iTheseBytesFreed = SND_FreeOldestSound()) != 0)
- {
- iBytesFreed += iTheseBytesFreed;
- if (iBytesFreed >= iRealSize)
- break; // early opt-out since we've managed to recover enough (mem-contiguity issues aside)
- }
- gbMemFreeupOccured = qtrue;
- continue;
- }
- }
- // sigh, dunno what else to try, I guess we'll have to give up and report this as an out-of-mem error...
- //
- // findlabel: "recovermem"
- Com_Printf(S_COLOR_RED"Z_Malloc(): Failed to alloc %d bytes (TAG_%s) !!!!!\n", iSize, psTagStrings[eTag]);
- Z_Details_f();
- Com_Error(ERR_FATAL,"(Repeat): Z_Malloc(): Failed to alloc %d bytes (TAG_%s) !!!!!\n", iSize, psTagStrings[eTag]);
- return NULL;
- }
- }
- #ifdef DEBUG_ZONE_ALLOCS
- extern char *Filename_WithoutPath(const char *psFilename);
- Q_strncpyz(pMemory->sSrcFileBaseName, Filename_WithoutPath(psFile), sizeof(pMemory->sSrcFileBaseName));
- pMemory->iSrcFileLineNum = iLine;
- pMemory->sOptionalLabel[0] = '\0';
- pMemory->iSnapshotNumber = giZoneSnaphotNum;
- #endif
- // Link in
- pMemory->iMagic = ZONE_MAGIC;
- pMemory->eTag = eTag;
- pMemory->iSize = iSize;
- pMemory->pNext = TheZone.Header.pNext;
- TheZone.Header.pNext = pMemory;
- if (pMemory->pNext)
- {
- pMemory->pNext->pPrev = pMemory;
- }
- pMemory->pPrev = &TheZone.Header;
- //
- // add tail...
- //
- ZoneTailFromHeader(pMemory)->iMagic = ZONE_MAGIC;
- // Update stats...
- //
- TheZone.Stats.iCurrent += iSize;
- TheZone.Stats.iCount++;
- TheZone.Stats.iSizesPerTag [eTag] += iSize;
- TheZone.Stats.iCountsPerTag [eTag]++;
- if (TheZone.Stats.iCurrent > TheZone.Stats.iPeak)
- {
- TheZone.Stats.iPeak = TheZone.Stats.iCurrent;
- }
- #ifdef DETAILED_ZONE_DEBUG_CODE
- mapAllocatedZones[pMemory]++;
- #endif
-
- Z_Validate(); // check for corruption
- void *pvReturnMem = &pMemory[1];
- return pvReturnMem;
- }
- // used during model cacheing to save an extra malloc, lets us morph the disk-load buffer then
- // just not fs_freefile() it afterwards.
- //
- void Z_MorphMallocTag( void *pvAddress, memtag_t eDesiredTag )
- {
- zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
- if (pMemory->iMagic != ZONE_MAGIC)
- {
- Com_Error(ERR_FATAL, "Z_MorphMallocTag(): Not a valid zone header!");
- return; // won't get here
- }
- // DEC existing tag stats...
- //
- // TheZone.Stats.iCurrent - unchanged
- // TheZone.Stats.iCount - unchanged
- TheZone.Stats.iSizesPerTag [pMemory->eTag] -= pMemory->iSize;
- TheZone.Stats.iCountsPerTag [pMemory->eTag]--;
- // morph...
- //
- pMemory->eTag = eDesiredTag;
- // INC new tag stats...
- //
- // TheZone.Stats.iCurrent - unchanged
- // TheZone.Stats.iCount - unchanged
- TheZone.Stats.iSizesPerTag [pMemory->eTag] += pMemory->iSize;
- TheZone.Stats.iCountsPerTag [pMemory->eTag]++;
- }
- static int Zone_FreeBlock(zoneHeader_t *pMemory)
- {
- const int iSize = pMemory->iSize;
- if (pMemory->eTag != TAG_STATIC) // belt and braces, should never hit this though
- {
- // Update stats...
- //
- TheZone.Stats.iCount--;
- TheZone.Stats.iCurrent -= pMemory->iSize;
- TheZone.Stats.iSizesPerTag [pMemory->eTag] -= pMemory->iSize;
- TheZone.Stats.iCountsPerTag [pMemory->eTag]--;
- // Sanity checks...
- //
- assert(pMemory->pPrev->pNext == pMemory);
- assert(!pMemory->pNext || (pMemory->pNext->pPrev == pMemory));
- // Unlink and free...
- //
- pMemory->pPrev->pNext = pMemory->pNext;
- if(pMemory->pNext)
- {
- pMemory->pNext->pPrev = pMemory->pPrev;
- }
-
- //debugging double frees
- pMemory->iMagic = 'FREE';
- free (pMemory);
-
- #ifdef DETAILED_ZONE_DEBUG_CODE
- // this has already been checked for in execution order, but wtf?
- int& iAllocCount = mapAllocatedZones[pMemory];
- if (iAllocCount == 0)
- {
- Com_Error(ERR_FATAL, "Zone_FreeBlock(): Double-freeing block!");
- return -1;
- }
- iAllocCount--;
- #endif
- }
- return iSize;
- }
- // stats-query function to to see if it's our malloc
- // returns block size if so
- qboolean Z_IsFromZone(void *pvAddress, memtag_t eTag)
- {
- zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
- #if 1 //debugging double free
- if (pMemory->iMagic == 'FREE')
- {
- Com_Printf("Z_IsFromZone(%x): Ptr has been freed already!(%9s)\n",pvAddress,pvAddress);
- return qfalse;
- }
- #endif
- if (pMemory->iMagic != ZONE_MAGIC)
- {
- return qfalse;
- }
- //looks like it is from our zone, let's double check the tag
- if (pMemory->eTag != eTag)
- {
- return qfalse;
- }
- return pMemory->iSize;
- }
- // stats-query function to ask how big a malloc is...
- //
- int Z_Size(void *pvAddress)
- {
- zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
- if (pMemory->eTag == TAG_STATIC)
- {
- return 0; // kind of
- }
- if (pMemory->iMagic != ZONE_MAGIC)
- {
- Com_Error(ERR_FATAL, "Z_Size(): Not a valid zone header!");
- return 0; // won't get here
- }
- return pMemory->iSize;
- }
- #ifdef DEBUG_ZONE_ALLOCS
- void _D_Z_Label(const void *pvAddress, const char *psLabel)
- {
- zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
- if (pMemory->eTag == TAG_STATIC)
- {
- return;
- }
- if (pMemory->iMagic != ZONE_MAGIC)
- {
- Com_Error(ERR_FATAL, "_D_Z_Label(): Not a valid zone header!");
- }
- Q_strncpyz( pMemory->sOptionalLabel, psLabel, sizeof(pMemory->sOptionalLabel));
- pMemory->sOptionalLabel[ sizeof(pMemory->sOptionalLabel)-1 ] = '\0';
- }
- #endif
- // Frees a block of memory...
- //
- int Z_Free(void *pvAddress)
- {
- if (!TheZone.Stats.iCount)
- {
- //Com_Error(ERR_FATAL, "Z_Free(): Zone has been cleard already!");
- Com_Printf("Z_Free(%x): Zone has been cleard already!\n",pvAddress);
- return -1;
- }
- zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
- #if 1 //debugging double free
- if (pMemory->iMagic == 'FREE')
- {
- Com_Error(ERR_FATAL, "Z_Free(%s): Block already-freed, or not allocated through Z_Malloc!",pvAddress);
- return -1;
- }
- #endif
- if (pMemory->eTag == TAG_STATIC)
- {
- return 0;
- }
- #ifdef DETAILED_ZONE_DEBUG_CODE
- //
- // check this error *before* barfing on bad magics...
- //
- int& iAllocCount = mapAllocatedZones[pMemory];
- if (iAllocCount <= 0)
- {
- Com_Error(ERR_FATAL, "Z_Free(): Block already-freed, or not allocated through Z_Malloc!");
- return -1;
- }
- #endif
- if (pMemory->iMagic != ZONE_MAGIC)
- {
- Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone header!");
- return -1;
- }
- if (ZoneTailFromHeader(pMemory)->iMagic != ZONE_MAGIC)
- {
- Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone tail!");
- return -1;
- }
- return Zone_FreeBlock(pMemory);
- }
- int Z_MemSize(memtag_t eTag)
- {
- return TheZone.Stats.iSizesPerTag[eTag];
- }
- // Frees all blocks with the specified tag...
- //
- void Z_TagFree(memtag_t eTag)
- {
- //#ifdef _DEBUG
- // int iZoneBlocks = TheZone.Stats.iCount;
- //#endif
- zoneHeader_t *pMemory = TheZone.Header.pNext;
- while (pMemory)
- {
- zoneHeader_t *pNext = pMemory->pNext;
- if ( (eTag == TAG_ALL) || (pMemory->eTag == eTag))
- {
- Zone_FreeBlock(pMemory);
- }
- pMemory = pNext;
- }
- // these stupid pragmas don't work here???!?!?!
- //
- //#ifdef _DEBUG
- //#pragma warning( disable : 4189)
- // int iBlocksFreed = iZoneBlocks - TheZone.Stats.iCount;
- //#pragma warning( default : 4189)
- //#endif
- }
- #ifdef DEBUG_ZONE_ALLOCS
- void *_D_S_Malloc ( int iSize, const char *psFile, int iLine)
- {
- return _D_Z_Malloc( iSize, TAG_SMALL, qfalse, psFile, iLine );
- }
- #else
- void *S_Malloc( int iSize )
- {
- return Z_Malloc( iSize, TAG_SMALL, qfalse);
- }
- #endif
- #ifdef _DEBUG
- static void Z_MemRecoverTest_f(void)
- {
- // needs to be in _DEBUG only, not good for final game!
- //
- if ( Cmd_Argc() != 2 ) {
- Com_Printf( "Usage: zone_memrecovertest max2alloc\n" );
- return;
- }
- int iMaxAlloc = 1024*1024*atoi( Cmd_Argv(1) );
- int iTotalMalloc = 0;
- while (1)
- {
- const int iThisMalloc = 5* (1024 * 1024);
- Z_Malloc(iThisMalloc, TAG_SPECIAL_MEM_TEST, qfalse); // and lose, just to consume memory
- iTotalMalloc += iThisMalloc;
- if (gbMemFreeupOccured || (iTotalMalloc >= iMaxAlloc) )
- break;
- }
- Z_TagFree(TAG_SPECIAL_MEM_TEST);
- }
- #endif
- // Gives a summary of the zone memory usage
- static void Z_Stats_f(void)
- {
- Com_Printf("\nThe zone is using %d bytes (%.2fMB) in %d memory blocks\n",
- TheZone.Stats.iCurrent,
- (float)TheZone.Stats.iCurrent / 1024.0f / 1024.0f,
- TheZone.Stats.iCount
- );
- Com_Printf("The zone peaked at %d bytes (%.2fMB)\n",
- TheZone.Stats.iPeak,
- (float)TheZone.Stats.iPeak / 1024.0f / 1024.0f
- );
- }
- // Gives a detailed breakdown of the memory blocks in the zone
- //
- static void Z_Details_f(void)
- {
- Com_Printf("---------------------------------------------------------------------------\n");
- Com_Printf("%20s %9s\n","Zone Tag","Bytes");
- Com_Printf("%20s %9s\n","--------","-----");
- for (int i=0; i<TAG_COUNT; i++)
- {
- int iThisCount = TheZone.Stats.iCountsPerTag[i];
- int iThisSize = TheZone.Stats.iSizesPerTag [i];
- if (iThisCount)
- {
- // can you believe that using %2.2f as a format specifier doesn't bloody work?
- // It ignores the left-hand specifier. Sigh, now I've got to do shit like this...
- //
- float fSize = (float)(iThisSize) / 1024.0f / 1024.0f;
- int iSize = fSize;
- int iRemainder = 100.0f * (fSize - floor(fSize));
- Com_Printf("%20s %9d (%2d.%02dMB) in %6d blocks (%9d Bytes/block)\n",
- psTagStrings[i],
- iThisSize,
- iSize,iRemainder,
- iThisCount, iThisSize / iThisCount
- );
- }
- }
- Com_Printf("---------------------------------------------------------------------------\n");
- Z_Stats_f();
- }
- #ifdef DEBUG_ZONE_ALLOCS
- #pragma warning (disable:4503) // decorated name length xceeded, name was truncated
- typedef map <sDebugString_t,int> LabelRefCount_t; // yet another place where Gil's tring class works and MS's doesn't
- typedef map <sDebugString_t,LabelRefCount_t> TagBlockLabels_t;
- TagBlockLabels_t AllTagBlockLabels;
- #pragma warning (disable:4503) // decorated name length xceeded, name was truncated
- static void Z_Snapshot_f(void)
- {
- AllTagBlockLabels.clear();
- zoneHeader_t *pMemory = TheZone.Header.pNext;
- while (pMemory)
- {
- AllTagBlockLabels[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]++;
- pMemory = pMemory->pNext;
- }
- giZoneSnaphotNum++;
- Com_Printf("Ok. ( Current snapshot num is now %d )\n",giZoneSnaphotNum);
- }
- static void Z_TagDebug_f(void)
- {
- TagBlockLabels_t AllTagBlockLabels_Local;
- qboolean bSnapShotTestActive = qfalse;
- memtag_t eTag = TAG_ALL;
- const char *psTAGName = Cmd_Argv(1);
- if (psTAGName[0])
- {
- // check optional arg...
- //
- if (!Q_stricmp(psTAGName,"#snap"))
- {
- bSnapShotTestActive = qtrue;
- AllTagBlockLabels_Local = AllTagBlockLabels; // horrible great STL copy
- psTAGName = Cmd_Argv(2);
- }
- if (psTAGName[0])
- {
- // skip over "tag_" if user supplied it...
- //
- if (!Q_stricmpn(psTAGName,"TAG_",4))
- {
- psTAGName += 4;
- }
- // see if the user specified a valid tag...
- //
- for (int i=0; i<TAG_COUNT; i++)
- {
- if (!Q_stricmp(psTAGName,psTagStrings[i]))
- {
- eTag = (memtag_t) i;
- break;
- }
- }
- }
- }
- else
- {
- Com_Printf("Usage: 'zone_tagdebug [#snap] <tag>', e.g. TAG_GHOUL2, TAG_ALL (careful!)\n");
- return;
- }
- Com_Printf("Dumping debug data for tag \"%s\"...%s\n\n",psTagStrings[eTag], bSnapShotTestActive?"( since snapshot only )":"");
- Com_Printf("%8s"," "); // to compensate for code further down: Com_Printf("(%5d) ",iBlocksListed);
- if (eTag == TAG_ALL)
- {
- Com_Printf("%20s ","Zone Tag");
- }
- Com_Printf("%9s\n","Bytes");
- Com_Printf("%8s"," ");
- if (eTag == TAG_ALL)
- {
- Com_Printf("%20s ","--------");
- }
- Com_Printf("%9s\n","-----");
- if (bSnapShotTestActive)
- {
- // dec ref counts in last snapshot for all current blocks (which will make new stuff go negative)
- //
- zoneHeader_t *pMemory = TheZone.Header.pNext;
- while (pMemory)
- {
- if (pMemory->eTag == eTag || eTag == TAG_ALL)
- {
- AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]--;
- }
- pMemory = pMemory->pNext;
- }
- }
- // now dump them out...
- //
- int iBlocksListed = 0;
- int iTotalSize = 0;
- zoneHeader_t *pMemory = TheZone.Header.pNext;
- while (pMemory)
- {
- if ( (pMemory->eTag == eTag || eTag == TAG_ALL)
- && (!bSnapShotTestActive || (pMemory->iSnapshotNumber == giZoneSnaphotNum && AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel] <0) )
- )
- {
- float fSize = (float)(pMemory->iSize) / 1024.0f / 1024.0f;
- int iSize = fSize;
- int iRemainder = 100.0f * (fSize - floor(fSize));
- Com_Printf("(%5d) ",iBlocksListed);
- if (eTag == TAG_ALL)
- {
- Com_Printf("%20s",psTagStrings[pMemory->eTag]);
- }
- Com_Printf(" %9d (%2d.%02dMB) File: \"%s\", Line: %d\n",
- pMemory->iSize,
- iSize,iRemainder,
- pMemory->sSrcFileBaseName,
- pMemory->iSrcFileLineNum
- );
- if (pMemory->sOptionalLabel[0])
- {
- Com_Printf("( Label: \"%s\" )\n",pMemory->sOptionalLabel);
- }
- iBlocksListed++;
- iTotalSize += pMemory->iSize;
-
- if (bSnapShotTestActive)
- {
- // bump ref count so we only 1 warning per new string, not for every one sharing that label...
- //
- AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]++;
- }
- }
- pMemory = pMemory->pNext;
- }
- Com_Printf("( %d blocks listed, %d bytes (%.2fMB) total )\n",iBlocksListed, iTotalSize, (float)iTotalSize / 1024.0f / 1024.0f);
- }
- #endif
- // Shuts down the zone memory system and frees up all memory
- void Com_ShutdownZoneMemory(void)
- {
- Cmd_RemoveCommand("zone_stats");
- Cmd_RemoveCommand("zone_details");
- #ifdef _DEBUG
- Cmd_RemoveCommand("zone_memrecovertest");
- #endif
- #ifdef DEBUG_ZONE_ALLOCS
- Cmd_RemoveCommand("zone_tagdebug");
- Cmd_RemoveCommand("zone_snapshot");
- #endif
- if(TheZone.Stats.iCount)
- {
- //Com_Printf("Automatically freeing %d blocks making up %d bytes\n", TheZone.Stats.iCount, TheZone.Stats.iCurrent);
- Z_TagFree(TAG_ALL);
- assert(!TheZone.Stats.iCount);
- assert(!TheZone.Stats.iCurrent);
- }
- }
- // Initialises the zone memory system
- void Com_InitZoneMemory( void )
- {
- Com_Printf("Initialising zone memory .....\n");
- memset(&TheZone, 0, sizeof(TheZone));
- TheZone.Header.iMagic = ZONE_MAGIC;
- com_validateZone = Cvar_Get("com_validateZone", "0", 0);
- Cmd_AddCommand("zone_stats", Z_Stats_f);
- Cmd_AddCommand("zone_details", Z_Details_f);
- #ifdef _DEBUG
- Cmd_AddCommand("zone_memrecovertest", Z_MemRecoverTest_f);
- #endif
- #ifdef DEBUG_ZONE_ALLOCS
- Cmd_AddCommand("zone_tagdebug", Z_TagDebug_f);
- Cmd_AddCommand("zone_snapshot", Z_Snapshot_f);
- #endif
- }
- /*
- ========================
- CopyString
- NOTE: never write over the memory CopyString returns because
- memory from a memstatic_t might be returned
- ========================
- */
- char *CopyString( const char *in ) {
- char *out;
- if (!in[0]) {
- return ((char *)&gEmptyString) + sizeof(zoneHeader_t);
- }
- else if (!in[1]) {
- if (in[0] >= '0' && in[0] <= '9') {
- return ((char *)&gNumberString[in[0]-'0']) + sizeof(zoneHeader_t);
- }
- }
- out = (char *) S_Malloc (strlen(in)+1);
- strcpy (out, in);
- Z_Label(out,in);
- return out;
- }
- /*
- ===============
- Com_TouchMemory
- Touch all known used data to make sure it is paged in
- ===============
- */
- void Com_TouchMemory( void ) {
- int start, end;
- int i, j;
- int sum;
- int totalTouched;
- Z_Validate();
- start = Sys_Milliseconds();
- sum = 0;
- totalTouched=0;
- zoneHeader_t *pMemory = TheZone.Header.pNext;
- while (pMemory)
- {
- byte *pMem = (byte *) &pMemory[1];
- j = pMemory->iSize >> 2;
- for (i=0; i<j; i+=64){
- sum += ((int*)pMem)[i];
- }
- totalTouched+=pMemory->iSize;
- pMemory = pMemory->pNext;
- }
- end = Sys_Milliseconds();
- //Com_Printf( "Com_TouchMemory: %i bytes, %i msec\n", totalTouched, end - start );
- }
|