Heap.cpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #ifndef USE_LIBC_MALLOC
  23. #define USE_LIBC_MALLOC 0
  24. #endif
  25. #ifndef CRASH_ON_STATIC_ALLOCATION
  26. // #define CRASH_ON_STATIC_ALLOCATION
  27. #endif
  28. //===============================================================
  29. //
  30. // idHeap
  31. //
  32. //===============================================================
  33. #define SMALL_HEADER_SIZE ( (int) ( sizeof( byte ) + sizeof( byte ) ) )
  34. #define MEDIUM_HEADER_SIZE ( (int) ( sizeof( mediumHeapEntry_s ) + sizeof( byte ) ) )
  35. #define LARGE_HEADER_SIZE ( (int) ( sizeof( dword * ) + sizeof( byte ) ) )
  36. #define ALIGN_SIZE( bytes ) ( ( (bytes) + ALIGN - 1 ) & ~(ALIGN - 1) )
  37. #define SMALL_ALIGN( bytes ) ( ALIGN_SIZE( (bytes) + SMALL_HEADER_SIZE ) - SMALL_HEADER_SIZE )
  38. #define MEDIUM_SMALLEST_SIZE ( ALIGN_SIZE( 256 ) + ALIGN_SIZE( MEDIUM_HEADER_SIZE ) )
  39. class idHeap {
  40. public:
  41. idHeap( void );
  42. ~idHeap( void ); // frees all associated data
  43. void Init( void ); // initialize
  44. void * Allocate( const dword bytes ); // allocate memory
  45. void Free( void *p ); // free memory
  46. void * Allocate16( const dword bytes );// allocate 16 byte aligned memory
  47. void Free16( void *p ); // free 16 byte aligned memory
  48. dword Msize( void *p ); // return size of data block
  49. void Dump( void );
  50. void AllocDefragBlock( void ); // hack for huge renderbumps
  51. private:
  52. enum {
  53. ALIGN = 8 // memory alignment in bytes
  54. };
  55. enum {
  56. INVALID_ALLOC = 0xdd,
  57. SMALL_ALLOC = 0xaa, // small allocation
  58. MEDIUM_ALLOC = 0xbb, // medium allocaction
  59. LARGE_ALLOC = 0xcc // large allocaction
  60. };
  61. struct page_s { // allocation page
  62. void * data; // data pointer to allocated memory
  63. dword dataSize; // number of bytes of memory 'data' points to
  64. page_s * next; // next free page in same page manager
  65. page_s * prev; // used only when allocated
  66. dword largestFree; // this data used by the medium-size heap manager
  67. void * firstFree; // pointer to first free entry
  68. };
  69. struct mediumHeapEntry_s {
  70. page_s * page; // pointer to page
  71. dword size; // size of block
  72. mediumHeapEntry_s * prev; // previous block
  73. mediumHeapEntry_s * next; // next block
  74. mediumHeapEntry_s * prevFree; // previous free block
  75. mediumHeapEntry_s * nextFree; // next free block
  76. dword freeBlock; // non-zero if free block
  77. };
  78. // variables
  79. void * smallFirstFree[256/ALIGN+1]; // small heap allocator lists (for allocs of 1-255 bytes)
  80. page_s * smallCurPage; // current page for small allocations
  81. dword smallCurPageOffset; // byte offset in current page
  82. page_s * smallFirstUsedPage; // first used page of the small heap manager
  83. page_s * mediumFirstFreePage; // first partially free page
  84. page_s * mediumLastFreePage; // last partially free page
  85. page_s * mediumFirstUsedPage; // completely used page
  86. page_s * largeFirstUsedPage; // first page used by the large heap manager
  87. page_s * swapPage;
  88. dword pagesAllocated; // number of pages currently allocated
  89. dword pageSize; // size of one alloc page in bytes
  90. dword pageRequests; // page requests
  91. dword OSAllocs; // number of allocs made to the OS
  92. int c_heapAllocRunningCount;
  93. void *defragBlock; // a single huge block that can be allocated
  94. // at startup, then freed when needed
  95. // methods
  96. page_s * AllocatePage( dword bytes ); // allocate page from the OS
  97. void FreePage( idHeap::page_s *p ); // free an OS allocated page
  98. void * SmallAllocate( dword bytes ); // allocate memory (1-255 bytes) from small heap manager
  99. void SmallFree( void *ptr ); // free memory allocated by small heap manager
  100. void * MediumAllocateFromPage( idHeap::page_s *p, dword sizeNeeded );
  101. void * MediumAllocate( dword bytes ); // allocate memory (256-32768 bytes) from medium heap manager
  102. void MediumFree( void *ptr ); // free memory allocated by medium heap manager
  103. void * LargeAllocate( dword bytes ); // allocate large block from OS directly
  104. void LargeFree( void *ptr ); // free memory allocated by large heap manager
  105. void ReleaseSwappedPages( void );
  106. void FreePageReal( idHeap::page_s *p );
  107. };
  108. /*
  109. ================
  110. idHeap::Init
  111. ================
  112. */
  113. void idHeap::Init () {
  114. OSAllocs = 0;
  115. pageRequests = 0;
  116. pageSize = 65536 - sizeof( idHeap::page_s );
  117. pagesAllocated = 0; // reset page allocation counter
  118. largeFirstUsedPage = NULL; // init large heap manager
  119. swapPage = NULL;
  120. memset( smallFirstFree, 0, sizeof(smallFirstFree) ); // init small heap manager
  121. smallFirstUsedPage = NULL;
  122. smallCurPage = AllocatePage( pageSize );
  123. assert( smallCurPage );
  124. smallCurPageOffset = SMALL_ALIGN( 0 );
  125. defragBlock = NULL;
  126. mediumFirstFreePage = NULL; // init medium heap manager
  127. mediumLastFreePage = NULL;
  128. mediumFirstUsedPage = NULL;
  129. c_heapAllocRunningCount = 0;
  130. }
  131. /*
  132. ================
  133. idHeap::idHeap
  134. ================
  135. */
  136. idHeap::idHeap( void ) {
  137. Init();
  138. }
  139. /*
  140. ================
  141. idHeap::~idHeap
  142. returns all allocated memory back to OS
  143. ================
  144. */
  145. idHeap::~idHeap( void ) {
  146. idHeap::page_s *p;
  147. if ( smallCurPage ) {
  148. FreePage( smallCurPage ); // free small-heap current allocation page
  149. }
  150. p = smallFirstUsedPage; // free small-heap allocated pages
  151. while( p ) {
  152. idHeap::page_s *next = p->next;
  153. FreePage( p );
  154. p= next;
  155. }
  156. p = largeFirstUsedPage; // free large-heap allocated pages
  157. while( p ) {
  158. idHeap::page_s *next = p->next;
  159. FreePage( p );
  160. p = next;
  161. }
  162. p = mediumFirstFreePage; // free medium-heap allocated pages
  163. while( p ) {
  164. idHeap::page_s *next = p->next;
  165. FreePage( p );
  166. p = next;
  167. }
  168. p = mediumFirstUsedPage; // free medium-heap allocated completely used pages
  169. while( p ) {
  170. idHeap::page_s *next = p->next;
  171. FreePage( p );
  172. p = next;
  173. }
  174. ReleaseSwappedPages();
  175. if ( defragBlock ) {
  176. free( defragBlock );
  177. }
  178. assert( pagesAllocated == 0 );
  179. }
  180. /*
  181. ================
  182. idHeap::AllocDefragBlock
  183. ================
  184. */
  185. void idHeap::AllocDefragBlock( void ) {
  186. int size = 0x40000000;
  187. if ( defragBlock ) {
  188. return;
  189. }
  190. while( 1 ) {
  191. defragBlock = malloc( size );
  192. if ( defragBlock ) {
  193. break;
  194. }
  195. size >>= 1;
  196. }
  197. idLib::common->Printf( "Allocated a %i mb defrag block\n", size / (1024*1024) );
  198. }
  199. /*
  200. ================
  201. idHeap::Allocate
  202. ================
  203. */
  204. void *idHeap::Allocate( const dword bytes ) {
  205. if ( !bytes ) {
  206. return NULL;
  207. }
  208. c_heapAllocRunningCount++;
  209. #if USE_LIBC_MALLOC
  210. return malloc( bytes );
  211. #else
  212. if ( !(bytes & ~255) ) {
  213. return SmallAllocate( bytes );
  214. }
  215. if ( !(bytes & ~32767) ) {
  216. return MediumAllocate( bytes );
  217. }
  218. return LargeAllocate( bytes );
  219. #endif
  220. }
  221. /*
  222. ================
  223. idHeap::Free
  224. ================
  225. */
  226. void idHeap::Free( void *p ) {
  227. if ( !p ) {
  228. return;
  229. }
  230. c_heapAllocRunningCount--;
  231. #if USE_LIBC_MALLOC
  232. free( p );
  233. #else
  234. switch( ((byte *)(p))[-1] ) {
  235. case SMALL_ALLOC: {
  236. SmallFree( p );
  237. break;
  238. }
  239. case MEDIUM_ALLOC: {
  240. MediumFree( p );
  241. break;
  242. }
  243. case LARGE_ALLOC: {
  244. LargeFree( p );
  245. break;
  246. }
  247. default: {
  248. idLib::common->FatalError( "idHeap::Free: invalid memory block (%s)", idLib::sys->GetCallStackCurStr( 4 ) );
  249. break;
  250. }
  251. }
  252. #endif
  253. }
  254. /*
  255. ================
  256. idHeap::Allocate16
  257. ================
  258. */
  259. void *idHeap::Allocate16( const dword bytes ) {
  260. byte *ptr, *alignedPtr;
  261. ptr = (byte *) malloc( bytes + 16 + 4 );
  262. if ( !ptr ) {
  263. if ( defragBlock ) {
  264. idLib::common->Printf( "Freeing defragBlock on alloc of %i.\n", bytes );
  265. free( defragBlock );
  266. defragBlock = NULL;
  267. ptr = (byte *) malloc( bytes + 16 + 4 );
  268. AllocDefragBlock();
  269. }
  270. if ( !ptr ) {
  271. common->FatalError( "malloc failure for %i", bytes );
  272. }
  273. }
  274. alignedPtr = (byte *) ( ( (int) ptr ) + 15 & ~15 );
  275. if ( alignedPtr - ptr < 4 ) {
  276. alignedPtr += 16;
  277. }
  278. *((int *)(alignedPtr - 4)) = (int) ptr;
  279. return (void *) alignedPtr;
  280. }
  281. /*
  282. ================
  283. idHeap::Free16
  284. ================
  285. */
  286. void idHeap::Free16( void *p ) {
  287. free( (void *) *((int *) (( (byte *) p ) - 4)) );
  288. }
  289. /*
  290. ================
  291. idHeap::Msize
  292. returns size of allocated memory block
  293. p = pointer to memory block
  294. Notes: size may not be the same as the size in the original
  295. allocation request (due to block alignment reasons).
  296. ================
  297. */
  298. dword idHeap::Msize( void *p ) {
  299. if ( !p ) {
  300. return 0;
  301. }
  302. #if USE_LIBC_MALLOC
  303. #ifdef _WIN32
  304. return _msize( p );
  305. #else
  306. return 0;
  307. #endif
  308. #else
  309. switch( ((byte *)(p))[-1] ) {
  310. case SMALL_ALLOC: {
  311. return SMALL_ALIGN( ((byte *)(p))[-SMALL_HEADER_SIZE] * ALIGN );
  312. }
  313. case MEDIUM_ALLOC: {
  314. return ((mediumHeapEntry_s *)(((byte *)(p)) - ALIGN_SIZE( MEDIUM_HEADER_SIZE )))->size - ALIGN_SIZE( MEDIUM_HEADER_SIZE );
  315. }
  316. case LARGE_ALLOC: {
  317. return ((idHeap::page_s*)(*((dword *)(((byte *)p) - ALIGN_SIZE( LARGE_HEADER_SIZE )))))->dataSize - ALIGN_SIZE( LARGE_HEADER_SIZE );
  318. }
  319. default: {
  320. idLib::common->FatalError( "idHeap::Msize: invalid memory block (%s)", idLib::sys->GetCallStackCurStr( 4 ) );
  321. return 0;
  322. }
  323. }
  324. #endif
  325. }
  326. /*
  327. ================
  328. idHeap::Dump
  329. dump contents of the heap
  330. ================
  331. */
  332. void idHeap::Dump( void ) {
  333. idHeap::page_s *pg;
  334. for ( pg = smallFirstUsedPage; pg; pg = pg->next ) {
  335. idLib::common->Printf( "%p bytes %-8d (in use by small heap)\n", pg->data, pg->dataSize);
  336. }
  337. if ( smallCurPage ) {
  338. pg = smallCurPage;
  339. idLib::common->Printf( "%p bytes %-8d (small heap active page)\n", pg->data, pg->dataSize );
  340. }
  341. for ( pg = mediumFirstUsedPage; pg; pg = pg->next ) {
  342. idLib::common->Printf( "%p bytes %-8d (completely used by medium heap)\n", pg->data, pg->dataSize );
  343. }
  344. for ( pg = mediumFirstFreePage; pg; pg = pg->next ) {
  345. idLib::common->Printf( "%p bytes %-8d (partially used by medium heap)\n", pg->data, pg->dataSize );
  346. }
  347. for ( pg = largeFirstUsedPage; pg; pg = pg->next ) {
  348. idLib::common->Printf( "%p bytes %-8d (fully used by large heap)\n", pg->data, pg->dataSize );
  349. }
  350. idLib::common->Printf( "pages allocated : %d\n", pagesAllocated );
  351. }
  352. /*
  353. ================
  354. idHeap::FreePageReal
  355. frees page to be used by the OS
  356. p = page to free
  357. ================
  358. */
  359. void idHeap::FreePageReal( idHeap::page_s *p ) {
  360. assert( p );
  361. ::free( p );
  362. }
  363. /*
  364. ================
  365. idHeap::ReleaseSwappedPages
  366. releases the swap page to OS
  367. ================
  368. */
  369. void idHeap::ReleaseSwappedPages () {
  370. if ( swapPage ) {
  371. FreePageReal( swapPage );
  372. }
  373. swapPage = NULL;
  374. }
  375. /*
  376. ================
  377. idHeap::AllocatePage
  378. allocates memory from the OS
  379. bytes = page size in bytes
  380. returns pointer to page
  381. ================
  382. */
  383. idHeap::page_s* idHeap::AllocatePage( dword bytes ) {
  384. idHeap::page_s* p;
  385. pageRequests++;
  386. if ( swapPage && swapPage->dataSize == bytes ) { // if we've got a swap page somewhere
  387. p = swapPage;
  388. swapPage = NULL;
  389. }
  390. else {
  391. dword size;
  392. size = bytes + sizeof(idHeap::page_s);
  393. p = (idHeap::page_s *) ::malloc( size + ALIGN - 1 );
  394. if ( !p ) {
  395. if ( defragBlock ) {
  396. idLib::common->Printf( "Freeing defragBlock on alloc of %i.\n", size + ALIGN - 1 );
  397. free( defragBlock );
  398. defragBlock = NULL;
  399. p = (idHeap::page_s *) ::malloc( size + ALIGN - 1 );
  400. AllocDefragBlock();
  401. }
  402. if ( !p ) {
  403. common->FatalError( "malloc failure for %i", bytes );
  404. }
  405. }
  406. p->data = (void *) ALIGN_SIZE( (int)((byte *)(p)) + sizeof( idHeap::page_s ) );
  407. p->dataSize = size - sizeof(idHeap::page_s);
  408. p->firstFree = NULL;
  409. p->largestFree = 0;
  410. OSAllocs++;
  411. }
  412. p->prev = NULL;
  413. p->next = NULL;
  414. pagesAllocated++;
  415. return p;
  416. }
  417. /*
  418. ================
  419. idHeap::FreePage
  420. frees a page back to the operating system
  421. p = pointer to page
  422. ================
  423. */
  424. void idHeap::FreePage( idHeap::page_s *p ) {
  425. assert( p );
  426. if ( p->dataSize == pageSize && !swapPage ) { // add to swap list?
  427. swapPage = p;
  428. }
  429. else {
  430. FreePageReal( p );
  431. }
  432. pagesAllocated--;
  433. }
  434. //===============================================================
  435. //
  436. // small heap code
  437. //
  438. //===============================================================
  439. /*
  440. ================
  441. idHeap::SmallAllocate
  442. allocate memory (1-255 bytes) from the small heap manager
  443. bytes = number of bytes to allocate
  444. returns pointer to allocated memory
  445. ================
  446. */
  447. void *idHeap::SmallAllocate( dword bytes ) {
  448. // we need the at least sizeof( dword ) bytes for the free list
  449. if ( bytes < sizeof( dword ) ) {
  450. bytes = sizeof( dword );
  451. }
  452. // increase the number of bytes if necessary to make sure the next small allocation is aligned
  453. bytes = SMALL_ALIGN( bytes );
  454. byte *smallBlock = (byte *)(smallFirstFree[bytes / ALIGN]);
  455. if ( smallBlock ) {
  456. dword *link = (dword *)(smallBlock + SMALL_HEADER_SIZE);
  457. smallBlock[1] = SMALL_ALLOC; // allocation identifier
  458. smallFirstFree[bytes / ALIGN] = (void *)(*link);
  459. return (void *)(link);
  460. }
  461. dword bytesLeft = (long)(pageSize) - smallCurPageOffset;
  462. // if we need to allocate a new page
  463. if ( bytes >= bytesLeft ) {
  464. smallCurPage->next = smallFirstUsedPage;
  465. smallFirstUsedPage = smallCurPage;
  466. smallCurPage = AllocatePage( pageSize );
  467. if ( !smallCurPage ) {
  468. return NULL;
  469. }
  470. // make sure the first allocation is aligned
  471. smallCurPageOffset = SMALL_ALIGN( 0 );
  472. }
  473. smallBlock = ((byte *)smallCurPage->data) + smallCurPageOffset;
  474. smallBlock[0] = (byte)(bytes / ALIGN); // write # of bytes/ALIGN
  475. smallBlock[1] = SMALL_ALLOC; // allocation identifier
  476. smallCurPageOffset += bytes + SMALL_HEADER_SIZE; // increase the offset on the current page
  477. return ( smallBlock + SMALL_HEADER_SIZE ); // skip the first two bytes
  478. }
  479. /*
  480. ================
  481. idHeap::SmallFree
  482. frees a block of memory allocated by SmallAllocate() call
  483. data = pointer to block of memory
  484. ================
  485. */
  486. void idHeap::SmallFree( void *ptr ) {
  487. ((byte *)(ptr))[-1] = INVALID_ALLOC;
  488. byte *d = ( (byte *)ptr ) - SMALL_HEADER_SIZE;
  489. dword *dt = (dword *)ptr;
  490. // index into the table with free small memory blocks
  491. dword ix = *d;
  492. // check if the index is correct
  493. if ( ix > (256 / ALIGN) ) {
  494. idLib::common->FatalError( "SmallFree: invalid memory block" );
  495. }
  496. *dt = (dword)smallFirstFree[ix]; // write next index
  497. smallFirstFree[ix] = (void *)d; // link
  498. }
  499. //===============================================================
  500. //
  501. // medium heap code
  502. //
  503. // Medium-heap allocated pages not returned to OS until heap destructor
  504. // called (re-used instead on subsequent medium-size malloc requests).
  505. //
  506. //===============================================================
  507. /*
  508. ================
  509. idHeap::MediumAllocateFromPage
  510. performs allocation using the medium heap manager from a given page
  511. p = page
  512. sizeNeeded = # of bytes needed
  513. returns pointer to allocated memory
  514. ================
  515. */
  516. void *idHeap::MediumAllocateFromPage( idHeap::page_s *p, dword sizeNeeded ) {
  517. mediumHeapEntry_s *best,*nw = NULL;
  518. byte *ret;
  519. best = (mediumHeapEntry_s *)(p->firstFree); // first block is largest
  520. assert( best );
  521. assert( best->size == p->largestFree );
  522. assert( best->size >= sizeNeeded );
  523. // if we can allocate another block from this page after allocating sizeNeeded bytes
  524. if ( best->size >= (dword)( sizeNeeded + MEDIUM_SMALLEST_SIZE ) ) {
  525. nw = (mediumHeapEntry_s *)((byte *)best + best->size - sizeNeeded);
  526. nw->page = p;
  527. nw->prev = best;
  528. nw->next = best->next;
  529. nw->prevFree = NULL;
  530. nw->nextFree = NULL;
  531. nw->size = sizeNeeded;
  532. nw->freeBlock = 0; // used block
  533. if ( best->next ) {
  534. best->next->prev = nw;
  535. }
  536. best->next = nw;
  537. best->size -= sizeNeeded;
  538. p->largestFree = best->size;
  539. }
  540. else {
  541. if ( best->prevFree ) {
  542. best->prevFree->nextFree = best->nextFree;
  543. }
  544. else {
  545. p->firstFree = (void *)best->nextFree;
  546. }
  547. if ( best->nextFree ) {
  548. best->nextFree->prevFree = best->prevFree;
  549. }
  550. best->prevFree = NULL;
  551. best->nextFree = NULL;
  552. best->freeBlock = 0; // used block
  553. nw = best;
  554. p->largestFree = 0;
  555. }
  556. ret = (byte *)(nw) + ALIGN_SIZE( MEDIUM_HEADER_SIZE );
  557. ret[-1] = MEDIUM_ALLOC; // allocation identifier
  558. return (void *)(ret);
  559. }
  560. /*
  561. ================
  562. idHeap::MediumAllocate
  563. allocate memory (256-32768 bytes) from medium heap manager
  564. bytes = number of bytes to allocate
  565. returns pointer to allocated memory
  566. ================
  567. */
  568. void *idHeap::MediumAllocate( dword bytes ) {
  569. idHeap::page_s *p;
  570. void *data;
  571. dword sizeNeeded = ALIGN_SIZE( bytes ) + ALIGN_SIZE( MEDIUM_HEADER_SIZE );
  572. // find first page with enough space
  573. for ( p = mediumFirstFreePage; p; p = p->next ) {
  574. if ( p->largestFree >= sizeNeeded ) {
  575. break;
  576. }
  577. }
  578. if ( !p ) { // need to allocate new page?
  579. p = AllocatePage( pageSize );
  580. if ( !p ) {
  581. return NULL; // malloc failure!
  582. }
  583. p->prev = NULL;
  584. p->next = mediumFirstFreePage;
  585. if (p->next) {
  586. p->next->prev = p;
  587. }
  588. else {
  589. mediumLastFreePage = p;
  590. }
  591. mediumFirstFreePage = p;
  592. p->largestFree = pageSize;
  593. p->firstFree = (void *)p->data;
  594. mediumHeapEntry_s *e;
  595. e = (mediumHeapEntry_s *)(p->firstFree);
  596. e->page = p;
  597. // make sure ((byte *)e + e->size) is aligned
  598. e->size = pageSize & ~(ALIGN - 1);
  599. e->prev = NULL;
  600. e->next = NULL;
  601. e->prevFree = NULL;
  602. e->nextFree = NULL;
  603. e->freeBlock = 1;
  604. }
  605. data = MediumAllocateFromPage( p, sizeNeeded ); // allocate data from page
  606. // if the page can no longer serve memory, move it away from free list
  607. // (so that it won't slow down the later alloc queries)
  608. // this modification speeds up the pageWalk from O(N) to O(sqrt(N))
  609. // a call to free may swap this page back to the free list
  610. if ( p->largestFree < MEDIUM_SMALLEST_SIZE ) {
  611. if ( p == mediumLastFreePage ) {
  612. mediumLastFreePage = p->prev;
  613. }
  614. if ( p == mediumFirstFreePage ) {
  615. mediumFirstFreePage = p->next;
  616. }
  617. if ( p->prev ) {
  618. p->prev->next = p->next;
  619. }
  620. if ( p->next ) {
  621. p->next->prev = p->prev;
  622. }
  623. // link to "completely used" list
  624. p->prev = NULL;
  625. p->next = mediumFirstUsedPage;
  626. if ( p->next ) {
  627. p->next->prev = p;
  628. }
  629. mediumFirstUsedPage = p;
  630. return data;
  631. }
  632. // re-order linked list (so that next malloc query starts from current
  633. // matching block) -- this speeds up both the page walks and block walks
  634. if ( p != mediumFirstFreePage ) {
  635. assert( mediumLastFreePage );
  636. assert( mediumFirstFreePage );
  637. assert( p->prev);
  638. mediumLastFreePage->next = mediumFirstFreePage;
  639. mediumFirstFreePage->prev = mediumLastFreePage;
  640. mediumLastFreePage = p->prev;
  641. p->prev->next = NULL;
  642. p->prev = NULL;
  643. mediumFirstFreePage = p;
  644. }
  645. return data;
  646. }
  647. /*
  648. ================
  649. idHeap::MediumFree
  650. frees a block allocated by the medium heap manager
  651. ptr = pointer to data block
  652. ================
  653. */
  654. void idHeap::MediumFree( void *ptr ) {
  655. ((byte *)(ptr))[-1] = INVALID_ALLOC;
  656. mediumHeapEntry_s *e = (mediumHeapEntry_s *)((byte *)ptr - ALIGN_SIZE( MEDIUM_HEADER_SIZE ));
  657. idHeap::page_s *p = e->page;
  658. bool isInFreeList;
  659. isInFreeList = p->largestFree >= MEDIUM_SMALLEST_SIZE;
  660. assert( e->size );
  661. assert( e->freeBlock == 0 );
  662. mediumHeapEntry_s *prev = e->prev;
  663. // if the previous block is free we can merge
  664. if ( prev && prev->freeBlock ) {
  665. prev->size += e->size;
  666. prev->next = e->next;
  667. if ( e->next ) {
  668. e->next->prev = prev;
  669. }
  670. e = prev;
  671. }
  672. else {
  673. e->prevFree = NULL; // link to beginning of free list
  674. e->nextFree = (mediumHeapEntry_s *)p->firstFree;
  675. if ( e->nextFree ) {
  676. assert( !(e->nextFree->prevFree) );
  677. e->nextFree->prevFree = e;
  678. }
  679. p->firstFree = e;
  680. p->largestFree = e->size;
  681. e->freeBlock = 1; // mark block as free
  682. }
  683. mediumHeapEntry_s *next = e->next;
  684. // if the next block is free we can merge
  685. if ( next && next->freeBlock ) {
  686. e->size += next->size;
  687. e->next = next->next;
  688. if ( next->next ) {
  689. next->next->prev = e;
  690. }
  691. if ( next->prevFree ) {
  692. next->prevFree->nextFree = next->nextFree;
  693. }
  694. else {
  695. assert( next == p->firstFree );
  696. p->firstFree = next->nextFree;
  697. }
  698. if ( next->nextFree ) {
  699. next->nextFree->prevFree = next->prevFree;
  700. }
  701. }
  702. if ( p->firstFree ) {
  703. p->largestFree = ((mediumHeapEntry_s *)(p->firstFree))->size;
  704. }
  705. else {
  706. p->largestFree = 0;
  707. }
  708. // did e become the largest block of the page ?
  709. if ( e->size > p->largestFree ) {
  710. assert( e != p->firstFree );
  711. p->largestFree = e->size;
  712. if ( e->prevFree ) {
  713. e->prevFree->nextFree = e->nextFree;
  714. }
  715. if ( e->nextFree ) {
  716. e->nextFree->prevFree = e->prevFree;
  717. }
  718. e->nextFree = (mediumHeapEntry_s *)p->firstFree;
  719. e->prevFree = NULL;
  720. if ( e->nextFree ) {
  721. e->nextFree->prevFree = e;
  722. }
  723. p->firstFree = e;
  724. }
  725. // if page wasn't in free list (because it was near-full), move it back there
  726. if ( !isInFreeList ) {
  727. // remove from "completely used" list
  728. if ( p->prev ) {
  729. p->prev->next = p->next;
  730. }
  731. if ( p->next ) {
  732. p->next->prev = p->prev;
  733. }
  734. if ( p == mediumFirstUsedPage ) {
  735. mediumFirstUsedPage = p->next;
  736. }
  737. p->next = NULL;
  738. p->prev = mediumLastFreePage;
  739. if ( mediumLastFreePage ) {
  740. mediumLastFreePage->next = p;
  741. }
  742. mediumLastFreePage = p;
  743. if ( !mediumFirstFreePage ) {
  744. mediumFirstFreePage = p;
  745. }
  746. }
  747. }
  748. //===============================================================
  749. //
  750. // large heap code
  751. //
  752. //===============================================================
  753. /*
  754. ================
  755. idHeap::LargeAllocate
  756. allocates a block of memory from the operating system
  757. bytes = number of bytes to allocate
  758. returns pointer to allocated memory
  759. ================
  760. */
  761. void *idHeap::LargeAllocate( dword bytes ) {
  762. idHeap::page_s *p = AllocatePage( bytes + ALIGN_SIZE( LARGE_HEADER_SIZE ) );
  763. assert( p );
  764. if ( !p ) {
  765. return NULL;
  766. }
  767. byte * d = (byte*)(p->data) + ALIGN_SIZE( LARGE_HEADER_SIZE );
  768. dword * dw = (dword*)(d - ALIGN_SIZE( LARGE_HEADER_SIZE ));
  769. dw[0] = (dword)p; // write pointer back to page table
  770. d[-1] = LARGE_ALLOC; // allocation identifier
  771. // link to 'large used page list'
  772. p->prev = NULL;
  773. p->next = largeFirstUsedPage;
  774. if ( p->next ) {
  775. p->next->prev = p;
  776. }
  777. largeFirstUsedPage = p;
  778. return (void *)(d);
  779. }
  780. /*
  781. ================
  782. idHeap::LargeFree
  783. frees a block of memory allocated by the 'large memory allocator'
  784. p = pointer to allocated memory
  785. ================
  786. */
  787. void idHeap::LargeFree( void *ptr) {
  788. idHeap::page_s* pg;
  789. ((byte *)(ptr))[-1] = INVALID_ALLOC;
  790. // get page pointer
  791. pg = (idHeap::page_s *)(*((dword *)(((byte *)ptr) - ALIGN_SIZE( LARGE_HEADER_SIZE ))));
  792. // unlink from doubly linked list
  793. if ( pg->prev ) {
  794. pg->prev->next = pg->next;
  795. }
  796. if ( pg->next ) {
  797. pg->next->prev = pg->prev;
  798. }
  799. if ( pg == largeFirstUsedPage ) {
  800. largeFirstUsedPage = pg->next;
  801. }
  802. pg->next = pg->prev = NULL;
  803. FreePage(pg);
  804. }
  805. //===============================================================
  806. //
  807. // memory allocation all in one place
  808. //
  809. //===============================================================
  810. #undef new
  811. static idHeap * mem_heap = NULL;
  812. static memoryStats_t mem_total_allocs = { 0, 0x0fffffff, -1, 0 };
  813. static memoryStats_t mem_frame_allocs;
  814. static memoryStats_t mem_frame_frees;
  815. /*
  816. ==================
  817. Mem_ClearFrameStats
  818. ==================
  819. */
  820. void Mem_ClearFrameStats( void ) {
  821. mem_frame_allocs.num = mem_frame_frees.num = 0;
  822. mem_frame_allocs.minSize = mem_frame_frees.minSize = 0x0fffffff;
  823. mem_frame_allocs.maxSize = mem_frame_frees.maxSize = -1;
  824. mem_frame_allocs.totalSize = mem_frame_frees.totalSize = 0;
  825. }
  826. /*
  827. ==================
  828. Mem_GetFrameStats
  829. ==================
  830. */
  831. void Mem_GetFrameStats( memoryStats_t &allocs, memoryStats_t &frees ) {
  832. allocs = mem_frame_allocs;
  833. frees = mem_frame_frees;
  834. }
  835. /*
  836. ==================
  837. Mem_GetStats
  838. ==================
  839. */
  840. void Mem_GetStats( memoryStats_t &stats ) {
  841. stats = mem_total_allocs;
  842. }
  843. /*
  844. ==================
  845. Mem_UpdateStats
  846. ==================
  847. */
  848. void Mem_UpdateStats( memoryStats_t &stats, int size ) {
  849. stats.num++;
  850. if ( size < stats.minSize ) {
  851. stats.minSize = size;
  852. }
  853. if ( size > stats.maxSize ) {
  854. stats.maxSize = size;
  855. }
  856. stats.totalSize += size;
  857. }
  858. /*
  859. ==================
  860. Mem_UpdateAllocStats
  861. ==================
  862. */
  863. void Mem_UpdateAllocStats( int size ) {
  864. Mem_UpdateStats( mem_frame_allocs, size );
  865. Mem_UpdateStats( mem_total_allocs, size );
  866. }
  867. /*
  868. ==================
  869. Mem_UpdateFreeStats
  870. ==================
  871. */
  872. void Mem_UpdateFreeStats( int size ) {
  873. Mem_UpdateStats( mem_frame_frees, size );
  874. mem_total_allocs.num--;
  875. mem_total_allocs.totalSize -= size;
  876. }
  877. #ifndef ID_DEBUG_MEMORY
  878. /*
  879. ==================
  880. Mem_Alloc
  881. ==================
  882. */
  883. void *Mem_Alloc( const int size ) {
  884. if ( !size ) {
  885. return NULL;
  886. }
  887. if ( !mem_heap ) {
  888. #ifdef CRASH_ON_STATIC_ALLOCATION
  889. *((int*)0x0) = 1;
  890. #endif
  891. return malloc( size );
  892. }
  893. void *mem = mem_heap->Allocate( size );
  894. Mem_UpdateAllocStats( mem_heap->Msize( mem ) );
  895. return mem;
  896. }
  897. /*
  898. ==================
  899. Mem_Free
  900. ==================
  901. */
  902. void Mem_Free( void *ptr ) {
  903. if ( !ptr ) {
  904. return;
  905. }
  906. if ( !mem_heap ) {
  907. #ifdef CRASH_ON_STATIC_ALLOCATION
  908. *((int*)0x0) = 1;
  909. #endif
  910. free( ptr );
  911. return;
  912. }
  913. Mem_UpdateFreeStats( mem_heap->Msize( ptr ) );
  914. mem_heap->Free( ptr );
  915. }
  916. /*
  917. ==================
  918. Mem_Alloc16
  919. ==================
  920. */
  921. void *Mem_Alloc16( const int size ) {
  922. if ( !size ) {
  923. return NULL;
  924. }
  925. if ( !mem_heap ) {
  926. #ifdef CRASH_ON_STATIC_ALLOCATION
  927. *((int*)0x0) = 1;
  928. #endif
  929. return malloc( size );
  930. }
  931. void *mem = mem_heap->Allocate16( size );
  932. // make sure the memory is 16 byte aligned
  933. assert( ( ((int)mem) & 15) == 0 );
  934. return mem;
  935. }
  936. /*
  937. ==================
  938. Mem_Free16
  939. ==================
  940. */
  941. void Mem_Free16( void *ptr ) {
  942. if ( !ptr ) {
  943. return;
  944. }
  945. if ( !mem_heap ) {
  946. #ifdef CRASH_ON_STATIC_ALLOCATION
  947. *((int*)0x0) = 1;
  948. #endif
  949. free( ptr );
  950. return;
  951. }
  952. // make sure the memory is 16 byte aligned
  953. assert( ( ((int)ptr) & 15) == 0 );
  954. mem_heap->Free16( ptr );
  955. }
  956. /*
  957. ==================
  958. Mem_ClearedAlloc
  959. ==================
  960. */
  961. void *Mem_ClearedAlloc( const int size ) {
  962. void *mem = Mem_Alloc( size );
  963. SIMDProcessor->Memset( mem, 0, size );
  964. return mem;
  965. }
  966. /*
  967. ==================
  968. Mem_ClearedAlloc
  969. ==================
  970. */
  971. void Mem_AllocDefragBlock( void ) {
  972. mem_heap->AllocDefragBlock();
  973. }
  974. /*
  975. ==================
  976. Mem_CopyString
  977. ==================
  978. */
  979. char *Mem_CopyString( const char *in ) {
  980. char *out;
  981. out = (char *)Mem_Alloc( strlen(in) + 1 );
  982. strcpy( out, in );
  983. return out;
  984. }
  985. /*
  986. ==================
  987. Mem_Dump_f
  988. ==================
  989. */
  990. void Mem_Dump_f( const idCmdArgs &args ) {
  991. }
  992. /*
  993. ==================
  994. Mem_DumpCompressed_f
  995. ==================
  996. */
  997. void Mem_DumpCompressed_f( const idCmdArgs &args ) {
  998. }
  999. /*
  1000. ==================
  1001. Mem_Init
  1002. ==================
  1003. */
  1004. void Mem_Init( void ) {
  1005. mem_heap = new idHeap;
  1006. Mem_ClearFrameStats();
  1007. }
  1008. /*
  1009. ==================
  1010. Mem_Shutdown
  1011. ==================
  1012. */
  1013. void Mem_Shutdown( void ) {
  1014. idHeap *m = mem_heap;
  1015. mem_heap = NULL;
  1016. delete m;
  1017. }
  1018. /*
  1019. ==================
  1020. Mem_EnableLeakTest
  1021. ==================
  1022. */
  1023. void Mem_EnableLeakTest( const char *name ) {
  1024. }
  1025. #else /* !ID_DEBUG_MEMORY */
  1026. #undef Mem_Alloc
  1027. #undef Mem_ClearedAlloc
  1028. #undef Com_ClearedReAlloc
  1029. #undef Mem_Free
  1030. #undef Mem_CopyString
  1031. #undef Mem_Alloc16
  1032. #undef Mem_Free16
  1033. #define MAX_CALLSTACK_DEPTH 6
  1034. // size of this struct must be a multiple of 16 bytes
  1035. typedef struct debugMemory_s {
  1036. const char * fileName;
  1037. int lineNumber;
  1038. int frameNumber;
  1039. int size;
  1040. address_t callStack[MAX_CALLSTACK_DEPTH];
  1041. struct debugMemory_s * prev;
  1042. struct debugMemory_s * next;
  1043. } debugMemory_t;
  1044. static debugMemory_t * mem_debugMemory = NULL;
  1045. static char mem_leakName[256] = "";
  1046. /*
  1047. ==================
  1048. Mem_CleanupFileName
  1049. ==================
  1050. */
  1051. const char *Mem_CleanupFileName( const char *fileName ) {
  1052. int i1, i2;
  1053. idStr newFileName;
  1054. static char newFileNames[4][MAX_STRING_CHARS];
  1055. static int index;
  1056. newFileName = fileName;
  1057. newFileName.BackSlashesToSlashes();
  1058. i1 = newFileName.Find( "neo", false );
  1059. if ( i1 >= 0 ) {
  1060. i1 = newFileName.Find( "/", false, i1 );
  1061. newFileName = newFileName.Right( newFileName.Length() - ( i1 + 1 ) );
  1062. }
  1063. while( 1 ) {
  1064. i1 = newFileName.Find( "/../" );
  1065. if ( i1 <= 0 ) {
  1066. break;
  1067. }
  1068. i2 = i1 - 1;
  1069. while( i2 > 1 && newFileName[i2-1] != '/' ) {
  1070. i2--;
  1071. }
  1072. newFileName = newFileName.Left( i2 - 1 ) + newFileName.Right( newFileName.Length() - ( i1 + 4 ) );
  1073. }
  1074. index = ( index + 1 ) & 3;
  1075. strncpy( newFileNames[index], newFileName.c_str(), sizeof( newFileNames[index] ) );
  1076. return newFileNames[index];
  1077. }
  1078. /*
  1079. ==================
  1080. Mem_Dump
  1081. ==================
  1082. */
  1083. void Mem_Dump( const char *fileName ) {
  1084. int i, numBlocks, totalSize;
  1085. char dump[32], *ptr;
  1086. debugMemory_t *b;
  1087. idStr module, funcName;
  1088. FILE *f;
  1089. f = fopen( fileName, "wb" );
  1090. if ( !f ) {
  1091. return;
  1092. }
  1093. totalSize = 0;
  1094. for ( numBlocks = 0, b = mem_debugMemory; b; b = b->next, numBlocks++ ) {
  1095. ptr = ((char *) b) + sizeof(debugMemory_t);
  1096. totalSize += b->size;
  1097. for ( i = 0; i < (sizeof(dump)-1) && i < b->size; i++) {
  1098. if ( ptr[i] >= 32 && ptr[i] < 127 ) {
  1099. dump[i] = ptr[i];
  1100. } else {
  1101. dump[i] = '_';
  1102. }
  1103. }
  1104. dump[i] = '\0';
  1105. if ( ( b->size >> 10 ) != 0 ) {
  1106. fprintf( f, "size: %6d KB: %s, line: %d [%s], call stack: %s\r\n", ( b->size >> 10 ), Mem_CleanupFileName(b->fileName), b->lineNumber, dump, idLib::sys->GetCallStackStr( b->callStack, MAX_CALLSTACK_DEPTH ) );
  1107. }
  1108. else {
  1109. fprintf( f, "size: %7d B: %s, line: %d [%s], call stack: %s\r\n", b->size, Mem_CleanupFileName(b->fileName), b->lineNumber, dump, idLib::sys->GetCallStackStr( b->callStack, MAX_CALLSTACK_DEPTH ) );
  1110. }
  1111. }
  1112. idLib::sys->ShutdownSymbols();
  1113. fprintf( f, "%8d total memory blocks allocated\r\n", numBlocks );
  1114. fprintf( f, "%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
  1115. fclose( f );
  1116. }
  1117. /*
  1118. ==================
  1119. Mem_Dump_f
  1120. ==================
  1121. */
  1122. void Mem_Dump_f( const idCmdArgs &args ) {
  1123. const char *fileName;
  1124. if ( args.Argc() >= 2 ) {
  1125. fileName = args.Argv( 1 );
  1126. }
  1127. else {
  1128. fileName = "memorydump.txt";
  1129. }
  1130. Mem_Dump( fileName );
  1131. }
  1132. /*
  1133. ==================
  1134. Mem_DumpCompressed
  1135. ==================
  1136. */
  1137. typedef struct allocInfo_s {
  1138. const char * fileName;
  1139. int lineNumber;
  1140. int size;
  1141. int numAllocs;
  1142. address_t callStack[MAX_CALLSTACK_DEPTH];
  1143. struct allocInfo_s * next;
  1144. } allocInfo_t;
  1145. typedef enum {
  1146. MEMSORT_SIZE,
  1147. MEMSORT_LOCATION,
  1148. MEMSORT_NUMALLOCS,
  1149. MEMSORT_CALLSTACK
  1150. } memorySortType_t;
  1151. void Mem_DumpCompressed( const char *fileName, memorySortType_t memSort, int sortCallStack, int numFrames ) {
  1152. int numBlocks, totalSize, r, j;
  1153. debugMemory_t *b;
  1154. allocInfo_t *a, *nexta, *allocInfo = NULL, *sortedAllocInfo = NULL, *prevSorted, *nextSorted;
  1155. idStr module, funcName;
  1156. FILE *f;
  1157. // build list with memory allocations
  1158. totalSize = 0;
  1159. numBlocks = 0;
  1160. for ( b = mem_debugMemory; b; b = b->next ) {
  1161. if ( numFrames && b->frameNumber < idLib::frameNumber - numFrames ) {
  1162. continue;
  1163. }
  1164. numBlocks++;
  1165. totalSize += b->size;
  1166. // search for an allocation from the same source location
  1167. for ( a = allocInfo; a; a = a->next ) {
  1168. if ( a->lineNumber != b->lineNumber ) {
  1169. continue;
  1170. }
  1171. for ( j = 0; j < MAX_CALLSTACK_DEPTH; j++ ) {
  1172. if ( a->callStack[j] != b->callStack[j] ) {
  1173. break;
  1174. }
  1175. }
  1176. if ( j < MAX_CALLSTACK_DEPTH ) {
  1177. continue;
  1178. }
  1179. if ( idStr::Cmp( a->fileName, b->fileName ) != 0 ) {
  1180. continue;
  1181. }
  1182. a->numAllocs++;
  1183. a->size += b->size;
  1184. break;
  1185. }
  1186. // if this is an allocation from a new source location
  1187. if ( !a ) {
  1188. a = (allocInfo_t *) ::malloc( sizeof( allocInfo_t ) );
  1189. a->fileName = b->fileName;
  1190. a->lineNumber = b->lineNumber;
  1191. a->size = b->size;
  1192. a->numAllocs = 1;
  1193. for ( j = 0; j < MAX_CALLSTACK_DEPTH; j++ ) {
  1194. a->callStack[j] = b->callStack[j];
  1195. }
  1196. a->next = allocInfo;
  1197. allocInfo = a;
  1198. }
  1199. }
  1200. // sort list
  1201. for ( a = allocInfo; a; a = nexta ) {
  1202. nexta = a->next;
  1203. prevSorted = NULL;
  1204. switch( memSort ) {
  1205. // sort on size
  1206. case MEMSORT_SIZE: {
  1207. for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
  1208. if ( a->size > nextSorted->size ) {
  1209. break;
  1210. }
  1211. prevSorted = nextSorted;
  1212. }
  1213. break;
  1214. }
  1215. // sort on file name and line number
  1216. case MEMSORT_LOCATION: {
  1217. for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
  1218. r = idStr::Cmp( Mem_CleanupFileName( a->fileName ), Mem_CleanupFileName( nextSorted->fileName ) );
  1219. if ( r < 0 || ( r == 0 && a->lineNumber < nextSorted->lineNumber ) ) {
  1220. break;
  1221. }
  1222. prevSorted = nextSorted;
  1223. }
  1224. break;
  1225. }
  1226. // sort on the number of allocations
  1227. case MEMSORT_NUMALLOCS: {
  1228. for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
  1229. if ( a->numAllocs > nextSorted->numAllocs ) {
  1230. break;
  1231. }
  1232. prevSorted = nextSorted;
  1233. }
  1234. break;
  1235. }
  1236. // sort on call stack
  1237. case MEMSORT_CALLSTACK: {
  1238. for ( nextSorted = sortedAllocInfo; nextSorted; nextSorted = nextSorted->next ) {
  1239. if ( a->callStack[sortCallStack] < nextSorted->callStack[sortCallStack] ) {
  1240. break;
  1241. }
  1242. prevSorted = nextSorted;
  1243. }
  1244. break;
  1245. }
  1246. }
  1247. if ( !prevSorted ) {
  1248. a->next = sortedAllocInfo;
  1249. sortedAllocInfo = a;
  1250. }
  1251. else {
  1252. prevSorted->next = a;
  1253. a->next = nextSorted;
  1254. }
  1255. }
  1256. f = fopen( fileName, "wb" );
  1257. if ( !f ) {
  1258. return;
  1259. }
  1260. // write list to file
  1261. for ( a = sortedAllocInfo; a; a = nexta ) {
  1262. nexta = a->next;
  1263. fprintf( f, "size: %6d KB, allocs: %5d: %s, line: %d, call stack: %s\r\n",
  1264. (a->size >> 10), a->numAllocs, Mem_CleanupFileName(a->fileName),
  1265. a->lineNumber, idLib::sys->GetCallStackStr( a->callStack, MAX_CALLSTACK_DEPTH ) );
  1266. ::free( a );
  1267. }
  1268. idLib::sys->ShutdownSymbols();
  1269. fprintf( f, "%8d total memory blocks allocated\r\n", numBlocks );
  1270. fprintf( f, "%8d KB memory allocated\r\n", ( totalSize >> 10 ) );
  1271. fclose( f );
  1272. }
  1273. /*
  1274. ==================
  1275. Mem_DumpCompressed_f
  1276. ==================
  1277. */
  1278. void Mem_DumpCompressed_f( const idCmdArgs &args ) {
  1279. int argNum;
  1280. const char *arg, *fileName;
  1281. memorySortType_t memSort = MEMSORT_LOCATION;
  1282. int sortCallStack = 0, numFrames = 0;
  1283. // get cmd-line options
  1284. argNum = 1;
  1285. arg = args.Argv( argNum );
  1286. while( arg[0] == '-' ) {
  1287. arg = args.Argv( ++argNum );
  1288. if ( idStr::Icmp( arg, "s" ) == 0 ) {
  1289. memSort = MEMSORT_SIZE;
  1290. } else if ( idStr::Icmp( arg, "l" ) == 0 ) {
  1291. memSort = MEMSORT_LOCATION;
  1292. } else if ( idStr::Icmp( arg, "a" ) == 0 ) {
  1293. memSort = MEMSORT_NUMALLOCS;
  1294. } else if ( idStr::Icmp( arg, "cs1" ) == 0 ) {
  1295. memSort = MEMSORT_CALLSTACK;
  1296. sortCallStack = 2;
  1297. } else if ( idStr::Icmp( arg, "cs2" ) == 0 ) {
  1298. memSort = MEMSORT_CALLSTACK;
  1299. sortCallStack = 1;
  1300. } else if ( idStr::Icmp( arg, "cs3" ) == 0 ) {
  1301. memSort = MEMSORT_CALLSTACK;
  1302. sortCallStack = 0;
  1303. } else if ( arg[0] == 'f' ) {
  1304. numFrames = atoi( arg + 1 );
  1305. } else {
  1306. idLib::common->Printf( "memoryDumpCompressed [options] [filename]\n"
  1307. "options:\n"
  1308. " -s sort on size\n"
  1309. " -l sort on location\n"
  1310. " -a sort on the number of allocations\n"
  1311. " -cs1 sort on first function on call stack\n"
  1312. " -cs2 sort on second function on call stack\n"
  1313. " -cs3 sort on third function on call stack\n"
  1314. " -f<X> only report allocations the last X frames\n"
  1315. "By default the memory allocations are sorted on location.\n"
  1316. "By default a 'memorydump.txt' is written if no file name is specified.\n" );
  1317. return;
  1318. }
  1319. arg = args.Argv( ++argNum );
  1320. }
  1321. if ( argNum >= args.Argc() ) {
  1322. fileName = "memorydump.txt";
  1323. } else {
  1324. fileName = arg;
  1325. }
  1326. Mem_DumpCompressed( fileName, memSort, sortCallStack, numFrames );
  1327. }
  1328. /*
  1329. ==================
  1330. Mem_AllocDebugMemory
  1331. ==================
  1332. */
  1333. void *Mem_AllocDebugMemory( const int size, const char *fileName, const int lineNumber, const bool align16 ) {
  1334. void *p;
  1335. debugMemory_t *m;
  1336. if ( !size ) {
  1337. return NULL;
  1338. }
  1339. if ( !mem_heap ) {
  1340. #ifdef CRASH_ON_STATIC_ALLOCATION
  1341. *((int*)0x0) = 1;
  1342. #endif
  1343. // NOTE: set a breakpoint here to find memory allocations before mem_heap is initialized
  1344. return malloc( size );
  1345. }
  1346. if ( align16 ) {
  1347. p = mem_heap->Allocate16( size + sizeof( debugMemory_t ) );
  1348. }
  1349. else {
  1350. p = mem_heap->Allocate( size + sizeof( debugMemory_t ) );
  1351. }
  1352. Mem_UpdateAllocStats( size );
  1353. m = (debugMemory_t *) p;
  1354. m->fileName = fileName;
  1355. m->lineNumber = lineNumber;
  1356. m->frameNumber = idLib::frameNumber;
  1357. m->size = size;
  1358. m->next = mem_debugMemory;
  1359. m->prev = NULL;
  1360. if ( mem_debugMemory ) {
  1361. mem_debugMemory->prev = m;
  1362. }
  1363. mem_debugMemory = m;
  1364. idLib::sys->GetCallStack( m->callStack, MAX_CALLSTACK_DEPTH );
  1365. return ( ( (byte *) p ) + sizeof( debugMemory_t ) );
  1366. }
  1367. /*
  1368. ==================
  1369. Mem_FreeDebugMemory
  1370. ==================
  1371. */
  1372. void Mem_FreeDebugMemory( void *p, const char *fileName, const int lineNumber, const bool align16 ) {
  1373. debugMemory_t *m;
  1374. if ( !p ) {
  1375. return;
  1376. }
  1377. if ( !mem_heap ) {
  1378. #ifdef CRASH_ON_STATIC_ALLOCATION
  1379. *((int*)0x0) = 1;
  1380. #endif
  1381. // NOTE: set a breakpoint here to find memory being freed before mem_heap is initialized
  1382. free( p );
  1383. return;
  1384. }
  1385. m = (debugMemory_t *) ( ( (byte *) p ) - sizeof( debugMemory_t ) );
  1386. if ( m->size < 0 ) {
  1387. idLib::common->FatalError( "memory freed twice, first from %s, now from %s", idLib::sys->GetCallStackStr( m->callStack, MAX_CALLSTACK_DEPTH ), idLib::sys->GetCallStackCurStr( MAX_CALLSTACK_DEPTH ) );
  1388. }
  1389. Mem_UpdateFreeStats( m->size );
  1390. if ( m->next ) {
  1391. m->next->prev = m->prev;
  1392. }
  1393. if ( m->prev ) {
  1394. m->prev->next = m->next;
  1395. }
  1396. else {
  1397. mem_debugMemory = m->next;
  1398. }
  1399. m->fileName = fileName;
  1400. m->lineNumber = lineNumber;
  1401. m->frameNumber = idLib::frameNumber;
  1402. m->size = -m->size;
  1403. idLib::sys->GetCallStack( m->callStack, MAX_CALLSTACK_DEPTH );
  1404. if ( align16 ) {
  1405. mem_heap->Free16( m );
  1406. }
  1407. else {
  1408. mem_heap->Free( m );
  1409. }
  1410. }
  1411. /*
  1412. ==================
  1413. Mem_Alloc
  1414. ==================
  1415. */
  1416. void *Mem_Alloc( const int size, const char *fileName, const int lineNumber ) {
  1417. if ( !size ) {
  1418. return NULL;
  1419. }
  1420. return Mem_AllocDebugMemory( size, fileName, lineNumber, false );
  1421. }
  1422. /*
  1423. ==================
  1424. Mem_Free
  1425. ==================
  1426. */
  1427. void Mem_Free( void *ptr, const char *fileName, const int lineNumber ) {
  1428. if ( !ptr ) {
  1429. return;
  1430. }
  1431. Mem_FreeDebugMemory( ptr, fileName, lineNumber, false );
  1432. }
  1433. /*
  1434. ==================
  1435. Mem_Alloc16
  1436. ==================
  1437. */
  1438. void *Mem_Alloc16( const int size, const char *fileName, const int lineNumber ) {
  1439. if ( !size ) {
  1440. return NULL;
  1441. }
  1442. void *mem = Mem_AllocDebugMemory( size, fileName, lineNumber, true );
  1443. // make sure the memory is 16 byte aligned
  1444. assert( ( ((int)mem) & 15) == 0 );
  1445. return mem;
  1446. }
  1447. /*
  1448. ==================
  1449. Mem_Free16
  1450. ==================
  1451. */
  1452. void Mem_Free16( void *ptr, const char *fileName, const int lineNumber ) {
  1453. if ( !ptr ) {
  1454. return;
  1455. }
  1456. // make sure the memory is 16 byte aligned
  1457. assert( ( ((int)ptr) & 15) == 0 );
  1458. Mem_FreeDebugMemory( ptr, fileName, lineNumber, true );
  1459. }
  1460. /*
  1461. ==================
  1462. Mem_ClearedAlloc
  1463. ==================
  1464. */
  1465. void *Mem_ClearedAlloc( const int size, const char *fileName, const int lineNumber ) {
  1466. void *mem = Mem_Alloc( size, fileName, lineNumber );
  1467. SIMDProcessor->Memset( mem, 0, size );
  1468. return mem;
  1469. }
  1470. /*
  1471. ==================
  1472. Mem_CopyString
  1473. ==================
  1474. */
  1475. char *Mem_CopyString( const char *in, const char *fileName, const int lineNumber ) {
  1476. char *out;
  1477. out = (char *)Mem_Alloc( strlen(in) + 1, fileName, lineNumber );
  1478. strcpy( out, in );
  1479. return out;
  1480. }
  1481. /*
  1482. ==================
  1483. Mem_Init
  1484. ==================
  1485. */
  1486. void Mem_Init( void ) {
  1487. mem_heap = new idHeap;
  1488. }
  1489. /*
  1490. ==================
  1491. Mem_Shutdown
  1492. ==================
  1493. */
  1494. void Mem_Shutdown( void ) {
  1495. if ( mem_leakName[0] != '\0' ) {
  1496. Mem_DumpCompressed( va( "%s_leak_size.txt", mem_leakName ), MEMSORT_SIZE, 0, 0 );
  1497. Mem_DumpCompressed( va( "%s_leak_location.txt", mem_leakName ), MEMSORT_LOCATION, 0, 0 );
  1498. Mem_DumpCompressed( va( "%s_leak_cs1.txt", mem_leakName ), MEMSORT_CALLSTACK, 2, 0 );
  1499. }
  1500. idHeap *m = mem_heap;
  1501. mem_heap = NULL;
  1502. delete m;
  1503. }
  1504. /*
  1505. ==================
  1506. Mem_EnableLeakTest
  1507. ==================
  1508. */
  1509. void Mem_EnableLeakTest( const char *name ) {
  1510. idStr::Copynz( mem_leakName, name, sizeof( mem_leakName ) );
  1511. }
  1512. #endif /* !ID_DEBUG_MEMORY */