z_memman_pc.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959
  1. // Created 2/3/03 by Brian Osman - split Zone code from common.cpp
  2. #include "../game/q_shared.h"
  3. #include "qcommon.h"
  4. #include "../qcommon/sstring.h"
  5. #include "platform.h"
  6. #ifdef DEBUG_ZONE_ALLOCS
  7. int giZoneSnaphotNum=0;
  8. #define DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE 256
  9. typedef sstring<DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE> sDebugString_t;
  10. #endif
  11. static void Z_Details_f(void);
  12. // define a string table of all mem tags...
  13. //
  14. #ifdef TAGDEF // itu?
  15. #undef TAGDEF
  16. #endif
  17. #define TAGDEF(blah) #blah
  18. static const char *psTagStrings[TAG_COUNT+1]= // +1 because TAG_COUNT will itself become a string here. Oh well.
  19. {
  20. #include "../qcommon/tags.h"
  21. };
  22. // This handles zone memory allocation.
  23. // It is a wrapper around malloc with a tag id and a magic number at the start
  24. #define ZONE_MAGIC 0x21436587
  25. // if you change ANYTHING in this structure, be sure to update the tables below using DEF_STATIC...
  26. //
  27. typedef struct zoneHeader_s
  28. {
  29. int iMagic;
  30. memtag_t eTag;
  31. int iSize;
  32. struct zoneHeader_s *pNext;
  33. struct zoneHeader_s *pPrev;
  34. #ifdef DEBUG_ZONE_ALLOCS
  35. char sSrcFileBaseName[MAX_QPATH];
  36. int iSrcFileLineNum;
  37. char sOptionalLabel[DEBUG_ZONE_ALLOC_OPTIONAL_LABEL_SIZE];
  38. int iSnapshotNumber;
  39. #endif
  40. } zoneHeader_t;
  41. typedef struct
  42. {
  43. int iMagic;
  44. } zoneTail_t;
  45. static inline zoneTail_t *ZoneTailFromHeader(zoneHeader_t *pHeader)
  46. {
  47. return (zoneTail_t*) ( (char*)pHeader + sizeof(*pHeader) + pHeader->iSize );
  48. }
  49. #ifdef DETAILED_ZONE_DEBUG_CODE
  50. map <void*,int> mapAllocatedZones;
  51. #endif
  52. typedef struct zoneStats_s
  53. {
  54. int iCount;
  55. int iCurrent;
  56. int iPeak;
  57. // I'm keeping these updated on the fly, since it's quicker for cache-pool
  58. // purposes rather than recalculating each time...
  59. //
  60. int iSizesPerTag [TAG_COUNT];
  61. int iCountsPerTag[TAG_COUNT];
  62. } zoneStats_t;
  63. typedef struct zone_s
  64. {
  65. zoneStats_t Stats;
  66. zoneHeader_t Header;
  67. } zone_t;
  68. cvar_t *com_validateZone;
  69. zone_t TheZone = {0};
  70. // Scans through the linked list of mallocs and makes sure no data has been overwritten
  71. int Z_Validate(void)
  72. {
  73. int ret=0;
  74. if(!com_validateZone || !com_validateZone->integer)
  75. {
  76. return ret;
  77. }
  78. zoneHeader_t *pMemory = TheZone.Header.pNext;
  79. while (pMemory)
  80. {
  81. #ifdef DETAILED_ZONE_DEBUG_CODE
  82. // this won't happen here, but wtf?
  83. int& iAllocCount = mapAllocatedZones[pMemory];
  84. if (iAllocCount <= 0)
  85. {
  86. Com_Error(ERR_FATAL, "Z_Validate(): Bad block allocation count!");
  87. return ret;
  88. }
  89. #endif
  90. if(pMemory->iMagic != ZONE_MAGIC)
  91. {
  92. Com_Error(ERR_FATAL, "Z_Validate(): Corrupt zone header!");
  93. return ret;
  94. }
  95. // this block of code is intended to make sure all of the data is paged in
  96. if (pMemory->eTag != TAG_IMAGE_T
  97. && pMemory->eTag != TAG_MODEL_MD3
  98. && pMemory->eTag != TAG_MODEL_GLM
  99. && pMemory->eTag != TAG_MODEL_GLA ) //don't bother with disk caches as they've already been hit or will be thrown out next
  100. {
  101. unsigned char *memstart = (unsigned char *)pMemory;
  102. int totalSize = pMemory->iSize;
  103. while (totalSize > 4096)
  104. {
  105. memstart += 4096;
  106. ret += (int)(*memstart); // this fools the optimizer
  107. totalSize -= 4096;
  108. }
  109. }
  110. if (ZoneTailFromHeader(pMemory)->iMagic != ZONE_MAGIC)
  111. {
  112. Com_Error(ERR_FATAL, "Z_Validate(): Corrupt zone tail!");
  113. return ret;
  114. }
  115. pMemory = pMemory->pNext;
  116. }
  117. return ret;
  118. }
  119. // static mem blocks to reduce a lot of small zone overhead
  120. //
  121. #pragma pack(push)
  122. #pragma pack(1)
  123. typedef struct
  124. {
  125. zoneHeader_t Header;
  126. // byte mem[0];
  127. zoneTail_t Tail;
  128. } StaticZeroMem_t;
  129. typedef struct
  130. {
  131. zoneHeader_t Header;
  132. byte mem[2];
  133. zoneTail_t Tail;
  134. } StaticMem_t;
  135. #pragma pack(pop)
  136. const static StaticZeroMem_t gZeroMalloc =
  137. { {ZONE_MAGIC, TAG_STATIC,0,NULL,NULL},{ZONE_MAGIC}};
  138. #ifdef DEBUG_ZONE_ALLOCS
  139. #define DEF_STATIC(_char) {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL, "<static>",0,"",0},_char,'\0',{ZONE_MAGIC}
  140. #else
  141. #define DEF_STATIC(_char) {ZONE_MAGIC, TAG_STATIC,2,NULL,NULL },_char,'\0',{ZONE_MAGIC}
  142. #endif
  143. const static StaticMem_t gEmptyString =
  144. { DEF_STATIC('\0') };
  145. const static StaticMem_t gNumberString[] = {
  146. { DEF_STATIC('0') },
  147. { DEF_STATIC('1') },
  148. { DEF_STATIC('2') },
  149. { DEF_STATIC('3') },
  150. { DEF_STATIC('4') },
  151. { DEF_STATIC('5') },
  152. { DEF_STATIC('6') },
  153. { DEF_STATIC('7') },
  154. { DEF_STATIC('8') },
  155. { DEF_STATIC('9') },
  156. };
  157. qboolean gbMemFreeupOccured = qfalse;
  158. #ifdef DEBUG_ZONE_ALLOCS
  159. void *_D_Z_Malloc ( int iSize, memtag_t eTag, qboolean bZeroit, const char *psFile, int iLine)
  160. #else
  161. void *Z_Malloc(int iSize, memtag_t eTag, qboolean bZeroit, int unusedAlign)
  162. #endif
  163. {
  164. gbMemFreeupOccured = qfalse;
  165. if (iSize == 0)
  166. {
  167. zoneHeader_t *pMemory = (zoneHeader_t *) &gZeroMalloc;
  168. return &pMemory[1];
  169. }
  170. // Add in tracking info and round to a longword... (ignore longword aligning now we're not using contiguous blocks)
  171. //
  172. // int iRealSize = (iSize + sizeof(zoneHeader_t) + sizeof(zoneTail_t) + 3) & 0xfffffffc;
  173. int iRealSize = (iSize + sizeof(zoneHeader_t) + sizeof(zoneTail_t));
  174. // Allocate a chunk...
  175. //
  176. zoneHeader_t *pMemory = NULL;
  177. while (pMemory == NULL)
  178. {
  179. #ifdef _WIN32
  180. if (gbMemFreeupOccured)
  181. {
  182. Sleep(1000); // sleep for a second, so Windows has a chance to shuffle mem to de-swiss-cheese it
  183. }
  184. #endif
  185. if (bZeroit) {
  186. pMemory = (zoneHeader_t *) calloc ( iRealSize, 1 );
  187. } else {
  188. pMemory = (zoneHeader_t *) malloc ( iRealSize );
  189. }
  190. if (!pMemory)
  191. {
  192. // new bit, if we fail to malloc memory, try dumping some of the cached stuff that's non-vital and try again...
  193. //
  194. // ditch the BSP cache...
  195. //
  196. if (CM_DeleteCachedMap(qfalse))
  197. {
  198. gbMemFreeupOccured = qtrue;
  199. continue; // we've just ditched a whole load of memory, so try again with the malloc
  200. }
  201. // ditch any sounds not used on this level...
  202. //
  203. extern qboolean SND_RegisterAudio_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel);
  204. if (SND_RegisterAudio_LevelLoadEnd(qtrue))
  205. {
  206. gbMemFreeupOccured = qtrue;
  207. continue; // we've dropped at least one sound, so try again with the malloc
  208. }
  209. // ditch any image_t's (and associated GL texture mem) not used on this level...
  210. //
  211. extern qboolean RE_RegisterImages_LevelLoadEnd(void);
  212. if (RE_RegisterImages_LevelLoadEnd())
  213. {
  214. gbMemFreeupOccured = qtrue;
  215. continue; // we've dropped at least one image, so try again with the malloc
  216. }
  217. // ditch the model-binaries cache... (must be getting desperate here!)
  218. //
  219. extern qboolean RE_RegisterModels_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel);
  220. if (RE_RegisterModels_LevelLoadEnd(qtrue))
  221. {
  222. gbMemFreeupOccured = qtrue;
  223. continue;
  224. }
  225. // as a last panic measure, dump all the audio memory, but not if we're in the audio loader
  226. // (which is annoying, but I'm not sure how to ensure we're not dumping any memory needed by the sound
  227. // currently being loaded if that was the case)...
  228. //
  229. // note that this keeps querying until it's freed up as many bytes as the requested size, but freeing
  230. // several small blocks might not mean that one larger one is satisfiable after freeup, however that'll
  231. // just make it go round again and try for freeing up another bunch of blocks until the total is satisfied
  232. // again (though this will have freed twice the requested amount in that case), so it'll either work
  233. // eventually or not free up enough and drop through to the final ERR_DROP. No worries...
  234. //
  235. extern qboolean gbInsideLoadSound;
  236. extern int SND_FreeOldestSound(void); // I had to add a void-arg version of this because of link issues, sigh
  237. if (!gbInsideLoadSound)
  238. {
  239. int iBytesFreed = SND_FreeOldestSound();
  240. if (iBytesFreed)
  241. {
  242. int iTheseBytesFreed = 0;
  243. while ( (iTheseBytesFreed = SND_FreeOldestSound()) != 0)
  244. {
  245. iBytesFreed += iTheseBytesFreed;
  246. if (iBytesFreed >= iRealSize)
  247. break; // early opt-out since we've managed to recover enough (mem-contiguity issues aside)
  248. }
  249. gbMemFreeupOccured = qtrue;
  250. continue;
  251. }
  252. }
  253. // sigh, dunno what else to try, I guess we'll have to give up and report this as an out-of-mem error...
  254. //
  255. // findlabel: "recovermem"
  256. Com_Printf(S_COLOR_RED"Z_Malloc(): Failed to alloc %d bytes (TAG_%s) !!!!!\n", iSize, psTagStrings[eTag]);
  257. Z_Details_f();
  258. Com_Error(ERR_FATAL,"(Repeat): Z_Malloc(): Failed to alloc %d bytes (TAG_%s) !!!!!\n", iSize, psTagStrings[eTag]);
  259. return NULL;
  260. }
  261. }
  262. #ifdef DEBUG_ZONE_ALLOCS
  263. extern char *Filename_WithoutPath(const char *psFilename);
  264. Q_strncpyz(pMemory->sSrcFileBaseName, Filename_WithoutPath(psFile), sizeof(pMemory->sSrcFileBaseName));
  265. pMemory->iSrcFileLineNum = iLine;
  266. pMemory->sOptionalLabel[0] = '\0';
  267. pMemory->iSnapshotNumber = giZoneSnaphotNum;
  268. #endif
  269. // Link in
  270. pMemory->iMagic = ZONE_MAGIC;
  271. pMemory->eTag = eTag;
  272. pMemory->iSize = iSize;
  273. pMemory->pNext = TheZone.Header.pNext;
  274. TheZone.Header.pNext = pMemory;
  275. if (pMemory->pNext)
  276. {
  277. pMemory->pNext->pPrev = pMemory;
  278. }
  279. pMemory->pPrev = &TheZone.Header;
  280. //
  281. // add tail...
  282. //
  283. ZoneTailFromHeader(pMemory)->iMagic = ZONE_MAGIC;
  284. // Update stats...
  285. //
  286. TheZone.Stats.iCurrent += iSize;
  287. TheZone.Stats.iCount++;
  288. TheZone.Stats.iSizesPerTag [eTag] += iSize;
  289. TheZone.Stats.iCountsPerTag [eTag]++;
  290. if (TheZone.Stats.iCurrent > TheZone.Stats.iPeak)
  291. {
  292. TheZone.Stats.iPeak = TheZone.Stats.iCurrent;
  293. }
  294. #ifdef DETAILED_ZONE_DEBUG_CODE
  295. mapAllocatedZones[pMemory]++;
  296. #endif
  297. Z_Validate(); // check for corruption
  298. void *pvReturnMem = &pMemory[1];
  299. return pvReturnMem;
  300. }
  301. // used during model cacheing to save an extra malloc, lets us morph the disk-load buffer then
  302. // just not fs_freefile() it afterwards.
  303. //
  304. void Z_MorphMallocTag( void *pvAddress, memtag_t eDesiredTag )
  305. {
  306. zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
  307. if (pMemory->iMagic != ZONE_MAGIC)
  308. {
  309. Com_Error(ERR_FATAL, "Z_MorphMallocTag(): Not a valid zone header!");
  310. return; // won't get here
  311. }
  312. // DEC existing tag stats...
  313. //
  314. // TheZone.Stats.iCurrent - unchanged
  315. // TheZone.Stats.iCount - unchanged
  316. TheZone.Stats.iSizesPerTag [pMemory->eTag] -= pMemory->iSize;
  317. TheZone.Stats.iCountsPerTag [pMemory->eTag]--;
  318. // morph...
  319. //
  320. pMemory->eTag = eDesiredTag;
  321. // INC new tag stats...
  322. //
  323. // TheZone.Stats.iCurrent - unchanged
  324. // TheZone.Stats.iCount - unchanged
  325. TheZone.Stats.iSizesPerTag [pMemory->eTag] += pMemory->iSize;
  326. TheZone.Stats.iCountsPerTag [pMemory->eTag]++;
  327. }
  328. static int Zone_FreeBlock(zoneHeader_t *pMemory)
  329. {
  330. const int iSize = pMemory->iSize;
  331. if (pMemory->eTag != TAG_STATIC) // belt and braces, should never hit this though
  332. {
  333. // Update stats...
  334. //
  335. TheZone.Stats.iCount--;
  336. TheZone.Stats.iCurrent -= pMemory->iSize;
  337. TheZone.Stats.iSizesPerTag [pMemory->eTag] -= pMemory->iSize;
  338. TheZone.Stats.iCountsPerTag [pMemory->eTag]--;
  339. // Sanity checks...
  340. //
  341. assert(pMemory->pPrev->pNext == pMemory);
  342. assert(!pMemory->pNext || (pMemory->pNext->pPrev == pMemory));
  343. // Unlink and free...
  344. //
  345. pMemory->pPrev->pNext = pMemory->pNext;
  346. if(pMemory->pNext)
  347. {
  348. pMemory->pNext->pPrev = pMemory->pPrev;
  349. }
  350. //debugging double frees
  351. pMemory->iMagic = 'FREE';
  352. free (pMemory);
  353. #ifdef DETAILED_ZONE_DEBUG_CODE
  354. // this has already been checked for in execution order, but wtf?
  355. int& iAllocCount = mapAllocatedZones[pMemory];
  356. if (iAllocCount == 0)
  357. {
  358. Com_Error(ERR_FATAL, "Zone_FreeBlock(): Double-freeing block!");
  359. return -1;
  360. }
  361. iAllocCount--;
  362. #endif
  363. }
  364. return iSize;
  365. }
  366. // stats-query function to to see if it's our malloc
  367. // returns block size if so
  368. qboolean Z_IsFromZone(void *pvAddress, memtag_t eTag)
  369. {
  370. zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
  371. #if 1 //debugging double free
  372. if (pMemory->iMagic == 'FREE')
  373. {
  374. Com_Printf("Z_IsFromZone(%x): Ptr has been freed already!(%9s)\n",pvAddress,pvAddress);
  375. return qfalse;
  376. }
  377. #endif
  378. if (pMemory->iMagic != ZONE_MAGIC)
  379. {
  380. return qfalse;
  381. }
  382. //looks like it is from our zone, let's double check the tag
  383. if (pMemory->eTag != eTag)
  384. {
  385. return qfalse;
  386. }
  387. return pMemory->iSize;
  388. }
  389. // stats-query function to ask how big a malloc is...
  390. //
  391. int Z_Size(void *pvAddress)
  392. {
  393. zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
  394. if (pMemory->eTag == TAG_STATIC)
  395. {
  396. return 0; // kind of
  397. }
  398. if (pMemory->iMagic != ZONE_MAGIC)
  399. {
  400. Com_Error(ERR_FATAL, "Z_Size(): Not a valid zone header!");
  401. return 0; // won't get here
  402. }
  403. return pMemory->iSize;
  404. }
  405. #ifdef DEBUG_ZONE_ALLOCS
  406. void _D_Z_Label(const void *pvAddress, const char *psLabel)
  407. {
  408. zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
  409. if (pMemory->eTag == TAG_STATIC)
  410. {
  411. return;
  412. }
  413. if (pMemory->iMagic != ZONE_MAGIC)
  414. {
  415. Com_Error(ERR_FATAL, "_D_Z_Label(): Not a valid zone header!");
  416. }
  417. Q_strncpyz( pMemory->sOptionalLabel, psLabel, sizeof(pMemory->sOptionalLabel));
  418. pMemory->sOptionalLabel[ sizeof(pMemory->sOptionalLabel)-1 ] = '\0';
  419. }
  420. #endif
  421. // Frees a block of memory...
  422. //
  423. int Z_Free(void *pvAddress)
  424. {
  425. if (!TheZone.Stats.iCount)
  426. {
  427. //Com_Error(ERR_FATAL, "Z_Free(): Zone has been cleard already!");
  428. Com_Printf("Z_Free(%x): Zone has been cleard already!\n",pvAddress);
  429. return -1;
  430. }
  431. zoneHeader_t *pMemory = ((zoneHeader_t *)pvAddress) - 1;
  432. #if 1 //debugging double free
  433. if (pMemory->iMagic == 'FREE')
  434. {
  435. Com_Error(ERR_FATAL, "Z_Free(%s): Block already-freed, or not allocated through Z_Malloc!",pvAddress);
  436. return -1;
  437. }
  438. #endif
  439. if (pMemory->eTag == TAG_STATIC)
  440. {
  441. return 0;
  442. }
  443. #ifdef DETAILED_ZONE_DEBUG_CODE
  444. //
  445. // check this error *before* barfing on bad magics...
  446. //
  447. int& iAllocCount = mapAllocatedZones[pMemory];
  448. if (iAllocCount <= 0)
  449. {
  450. Com_Error(ERR_FATAL, "Z_Free(): Block already-freed, or not allocated through Z_Malloc!");
  451. return -1;
  452. }
  453. #endif
  454. if (pMemory->iMagic != ZONE_MAGIC)
  455. {
  456. Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone header!");
  457. return -1;
  458. }
  459. if (ZoneTailFromHeader(pMemory)->iMagic != ZONE_MAGIC)
  460. {
  461. Com_Error(ERR_FATAL, "Z_Free(): Corrupt zone tail!");
  462. return -1;
  463. }
  464. return Zone_FreeBlock(pMemory);
  465. }
  466. int Z_MemSize(memtag_t eTag)
  467. {
  468. return TheZone.Stats.iSizesPerTag[eTag];
  469. }
  470. // Frees all blocks with the specified tag...
  471. //
  472. void Z_TagFree(memtag_t eTag)
  473. {
  474. //#ifdef _DEBUG
  475. // int iZoneBlocks = TheZone.Stats.iCount;
  476. //#endif
  477. zoneHeader_t *pMemory = TheZone.Header.pNext;
  478. while (pMemory)
  479. {
  480. zoneHeader_t *pNext = pMemory->pNext;
  481. if ( (eTag == TAG_ALL) || (pMemory->eTag == eTag))
  482. {
  483. Zone_FreeBlock(pMemory);
  484. }
  485. pMemory = pNext;
  486. }
  487. // these stupid pragmas don't work here???!?!?!
  488. //
  489. //#ifdef _DEBUG
  490. //#pragma warning( disable : 4189)
  491. // int iBlocksFreed = iZoneBlocks - TheZone.Stats.iCount;
  492. //#pragma warning( default : 4189)
  493. //#endif
  494. }
  495. #ifdef DEBUG_ZONE_ALLOCS
  496. void *_D_S_Malloc ( int iSize, const char *psFile, int iLine)
  497. {
  498. return _D_Z_Malloc( iSize, TAG_SMALL, qfalse, psFile, iLine );
  499. }
  500. #else
  501. void *S_Malloc( int iSize )
  502. {
  503. return Z_Malloc( iSize, TAG_SMALL, qfalse);
  504. }
  505. #endif
  506. #ifdef _DEBUG
  507. static void Z_MemRecoverTest_f(void)
  508. {
  509. // needs to be in _DEBUG only, not good for final game!
  510. //
  511. if ( Cmd_Argc() != 2 ) {
  512. Com_Printf( "Usage: zone_memrecovertest max2alloc\n" );
  513. return;
  514. }
  515. int iMaxAlloc = 1024*1024*atoi( Cmd_Argv(1) );
  516. int iTotalMalloc = 0;
  517. while (1)
  518. {
  519. const int iThisMalloc = 5* (1024 * 1024);
  520. Z_Malloc(iThisMalloc, TAG_SPECIAL_MEM_TEST, qfalse); // and lose, just to consume memory
  521. iTotalMalloc += iThisMalloc;
  522. if (gbMemFreeupOccured || (iTotalMalloc >= iMaxAlloc) )
  523. break;
  524. }
  525. Z_TagFree(TAG_SPECIAL_MEM_TEST);
  526. }
  527. #endif
  528. // Gives a summary of the zone memory usage
  529. static void Z_Stats_f(void)
  530. {
  531. Com_Printf("\nThe zone is using %d bytes (%.2fMB) in %d memory blocks\n",
  532. TheZone.Stats.iCurrent,
  533. (float)TheZone.Stats.iCurrent / 1024.0f / 1024.0f,
  534. TheZone.Stats.iCount
  535. );
  536. Com_Printf("The zone peaked at %d bytes (%.2fMB)\n",
  537. TheZone.Stats.iPeak,
  538. (float)TheZone.Stats.iPeak / 1024.0f / 1024.0f
  539. );
  540. }
  541. // Gives a detailed breakdown of the memory blocks in the zone
  542. //
  543. static void Z_Details_f(void)
  544. {
  545. Com_Printf("---------------------------------------------------------------------------\n");
  546. Com_Printf("%20s %9s\n","Zone Tag","Bytes");
  547. Com_Printf("%20s %9s\n","--------","-----");
  548. for (int i=0; i<TAG_COUNT; i++)
  549. {
  550. int iThisCount = TheZone.Stats.iCountsPerTag[i];
  551. int iThisSize = TheZone.Stats.iSizesPerTag [i];
  552. if (iThisCount)
  553. {
  554. // can you believe that using %2.2f as a format specifier doesn't bloody work?
  555. // It ignores the left-hand specifier. Sigh, now I've got to do shit like this...
  556. //
  557. float fSize = (float)(iThisSize) / 1024.0f / 1024.0f;
  558. int iSize = fSize;
  559. int iRemainder = 100.0f * (fSize - floor(fSize));
  560. Com_Printf("%20s %9d (%2d.%02dMB) in %6d blocks (%9d Bytes/block)\n",
  561. psTagStrings[i],
  562. iThisSize,
  563. iSize,iRemainder,
  564. iThisCount, iThisSize / iThisCount
  565. );
  566. }
  567. }
  568. Com_Printf("---------------------------------------------------------------------------\n");
  569. Z_Stats_f();
  570. }
  571. #ifdef DEBUG_ZONE_ALLOCS
  572. #pragma warning (disable:4503) // decorated name length xceeded, name was truncated
  573. typedef map <sDebugString_t,int> LabelRefCount_t; // yet another place where Gil's tring class works and MS's doesn't
  574. typedef map <sDebugString_t,LabelRefCount_t> TagBlockLabels_t;
  575. TagBlockLabels_t AllTagBlockLabels;
  576. #pragma warning (disable:4503) // decorated name length xceeded, name was truncated
  577. static void Z_Snapshot_f(void)
  578. {
  579. AllTagBlockLabels.clear();
  580. zoneHeader_t *pMemory = TheZone.Header.pNext;
  581. while (pMemory)
  582. {
  583. AllTagBlockLabels[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]++;
  584. pMemory = pMemory->pNext;
  585. }
  586. giZoneSnaphotNum++;
  587. Com_Printf("Ok. ( Current snapshot num is now %d )\n",giZoneSnaphotNum);
  588. }
  589. static void Z_TagDebug_f(void)
  590. {
  591. TagBlockLabels_t AllTagBlockLabels_Local;
  592. qboolean bSnapShotTestActive = qfalse;
  593. memtag_t eTag = TAG_ALL;
  594. const char *psTAGName = Cmd_Argv(1);
  595. if (psTAGName[0])
  596. {
  597. // check optional arg...
  598. //
  599. if (!Q_stricmp(psTAGName,"#snap"))
  600. {
  601. bSnapShotTestActive = qtrue;
  602. AllTagBlockLabels_Local = AllTagBlockLabels; // horrible great STL copy
  603. psTAGName = Cmd_Argv(2);
  604. }
  605. if (psTAGName[0])
  606. {
  607. // skip over "tag_" if user supplied it...
  608. //
  609. if (!Q_stricmpn(psTAGName,"TAG_",4))
  610. {
  611. psTAGName += 4;
  612. }
  613. // see if the user specified a valid tag...
  614. //
  615. for (int i=0; i<TAG_COUNT; i++)
  616. {
  617. if (!Q_stricmp(psTAGName,psTagStrings[i]))
  618. {
  619. eTag = (memtag_t) i;
  620. break;
  621. }
  622. }
  623. }
  624. }
  625. else
  626. {
  627. Com_Printf("Usage: 'zone_tagdebug [#snap] <tag>', e.g. TAG_GHOUL2, TAG_ALL (careful!)\n");
  628. return;
  629. }
  630. Com_Printf("Dumping debug data for tag \"%s\"...%s\n\n",psTagStrings[eTag], bSnapShotTestActive?"( since snapshot only )":"");
  631. Com_Printf("%8s"," "); // to compensate for code further down: Com_Printf("(%5d) ",iBlocksListed);
  632. if (eTag == TAG_ALL)
  633. {
  634. Com_Printf("%20s ","Zone Tag");
  635. }
  636. Com_Printf("%9s\n","Bytes");
  637. Com_Printf("%8s"," ");
  638. if (eTag == TAG_ALL)
  639. {
  640. Com_Printf("%20s ","--------");
  641. }
  642. Com_Printf("%9s\n","-----");
  643. if (bSnapShotTestActive)
  644. {
  645. // dec ref counts in last snapshot for all current blocks (which will make new stuff go negative)
  646. //
  647. zoneHeader_t *pMemory = TheZone.Header.pNext;
  648. while (pMemory)
  649. {
  650. if (pMemory->eTag == eTag || eTag == TAG_ALL)
  651. {
  652. AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]--;
  653. }
  654. pMemory = pMemory->pNext;
  655. }
  656. }
  657. // now dump them out...
  658. //
  659. int iBlocksListed = 0;
  660. int iTotalSize = 0;
  661. zoneHeader_t *pMemory = TheZone.Header.pNext;
  662. while (pMemory)
  663. {
  664. if ( (pMemory->eTag == eTag || eTag == TAG_ALL)
  665. && (!bSnapShotTestActive || (pMemory->iSnapshotNumber == giZoneSnaphotNum && AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel] <0) )
  666. )
  667. {
  668. float fSize = (float)(pMemory->iSize) / 1024.0f / 1024.0f;
  669. int iSize = fSize;
  670. int iRemainder = 100.0f * (fSize - floor(fSize));
  671. Com_Printf("(%5d) ",iBlocksListed);
  672. if (eTag == TAG_ALL)
  673. {
  674. Com_Printf("%20s",psTagStrings[pMemory->eTag]);
  675. }
  676. Com_Printf(" %9d (%2d.%02dMB) File: \"%s\", Line: %d\n",
  677. pMemory->iSize,
  678. iSize,iRemainder,
  679. pMemory->sSrcFileBaseName,
  680. pMemory->iSrcFileLineNum
  681. );
  682. if (pMemory->sOptionalLabel[0])
  683. {
  684. Com_Printf("( Label: \"%s\" )\n",pMemory->sOptionalLabel);
  685. }
  686. iBlocksListed++;
  687. iTotalSize += pMemory->iSize;
  688. if (bSnapShotTestActive)
  689. {
  690. // bump ref count so we only 1 warning per new string, not for every one sharing that label...
  691. //
  692. AllTagBlockLabels_Local[psTagStrings[pMemory->eTag]][pMemory->sOptionalLabel]++;
  693. }
  694. }
  695. pMemory = pMemory->pNext;
  696. }
  697. Com_Printf("( %d blocks listed, %d bytes (%.2fMB) total )\n",iBlocksListed, iTotalSize, (float)iTotalSize / 1024.0f / 1024.0f);
  698. }
  699. #endif
  700. // Shuts down the zone memory system and frees up all memory
  701. void Com_ShutdownZoneMemory(void)
  702. {
  703. Cmd_RemoveCommand("zone_stats");
  704. Cmd_RemoveCommand("zone_details");
  705. #ifdef _DEBUG
  706. Cmd_RemoveCommand("zone_memrecovertest");
  707. #endif
  708. #ifdef DEBUG_ZONE_ALLOCS
  709. Cmd_RemoveCommand("zone_tagdebug");
  710. Cmd_RemoveCommand("zone_snapshot");
  711. #endif
  712. if(TheZone.Stats.iCount)
  713. {
  714. //Com_Printf("Automatically freeing %d blocks making up %d bytes\n", TheZone.Stats.iCount, TheZone.Stats.iCurrent);
  715. Z_TagFree(TAG_ALL);
  716. assert(!TheZone.Stats.iCount);
  717. assert(!TheZone.Stats.iCurrent);
  718. }
  719. }
  720. // Initialises the zone memory system
  721. void Com_InitZoneMemory( void )
  722. {
  723. Com_Printf("Initialising zone memory .....\n");
  724. memset(&TheZone, 0, sizeof(TheZone));
  725. TheZone.Header.iMagic = ZONE_MAGIC;
  726. com_validateZone = Cvar_Get("com_validateZone", "0", 0);
  727. Cmd_AddCommand("zone_stats", Z_Stats_f);
  728. Cmd_AddCommand("zone_details", Z_Details_f);
  729. #ifdef _DEBUG
  730. Cmd_AddCommand("zone_memrecovertest", Z_MemRecoverTest_f);
  731. #endif
  732. #ifdef DEBUG_ZONE_ALLOCS
  733. Cmd_AddCommand("zone_tagdebug", Z_TagDebug_f);
  734. Cmd_AddCommand("zone_snapshot", Z_Snapshot_f);
  735. #endif
  736. }
  737. /*
  738. ========================
  739. CopyString
  740. NOTE: never write over the memory CopyString returns because
  741. memory from a memstatic_t might be returned
  742. ========================
  743. */
  744. char *CopyString( const char *in ) {
  745. char *out;
  746. if (!in[0]) {
  747. return ((char *)&gEmptyString) + sizeof(zoneHeader_t);
  748. }
  749. else if (!in[1]) {
  750. if (in[0] >= '0' && in[0] <= '9') {
  751. return ((char *)&gNumberString[in[0]-'0']) + sizeof(zoneHeader_t);
  752. }
  753. }
  754. out = (char *) S_Malloc (strlen(in)+1);
  755. strcpy (out, in);
  756. Z_Label(out,in);
  757. return out;
  758. }
  759. /*
  760. ===============
  761. Com_TouchMemory
  762. Touch all known used data to make sure it is paged in
  763. ===============
  764. */
  765. void Com_TouchMemory( void ) {
  766. int start, end;
  767. int i, j;
  768. int sum;
  769. int totalTouched;
  770. Z_Validate();
  771. start = Sys_Milliseconds();
  772. sum = 0;
  773. totalTouched=0;
  774. zoneHeader_t *pMemory = TheZone.Header.pNext;
  775. while (pMemory)
  776. {
  777. byte *pMem = (byte *) &pMemory[1];
  778. j = pMemory->iSize >> 2;
  779. for (i=0; i<j; i+=64){
  780. sum += ((int*)pMem)[i];
  781. }
  782. totalTouched+=pMemory->iSize;
  783. pMemory = pMemory->pNext;
  784. }
  785. end = Sys_Milliseconds();
  786. //Com_Printf( "Com_TouchMemory: %i bytes, %i msec\n", totalTouched, end - start );
  787. }