| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: Memory.cpp
- //-----------------------------------------------------------------------------
- //
- // Westwood Studios Pacific.
- //
- // Confidential Information
- // Copyright (C) 2001 - All Rights Reserved
- //
- //-----------------------------------------------------------------------------
- //
- // Project: RTS3
- //
- // File name: Memory.cpp
- //
- // Created: Steven Johnson, August 2001
- //
- // Desc: Memory manager
- //
- // ----------------------------------------------------------------------------
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- // SYSTEM INCLUDES
- // USER INCLUDES
- #include "Common/GameMemory.h"
- #include "Common/CriticalSection.h"
- #include "Common/Errors.h"
- #include "Common/GlobalData.h"
- #include "Common/PerfTimer.h"
- #ifdef MEMORYPOOL_DEBUG
- #include "GameClient/ClientRandomValue.h"
- #endif
- #ifdef MEMORYPOOL_STACKTRACE
- #include "Common/StackDump.h"
- #endif
- #ifdef MEMORYPOOL_DEBUG
- DECLARE_PERF_TIMER(MemoryPoolDebugging)
- DECLARE_PERF_TIMER(MemoryPoolInitFilling)
- #endif
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // ----------------------------------------------------------------------------
- // DEFINES
- // ----------------------------------------------------------------------------
- /**
- define MPSB_DLINK to add a backlink to MemoryPoolSingleBlock; this makes it
- faster to free raw DMA blocks.
- @todo verify this speedup is enough to be worth the extra space
- */
- #define MPSB_DLINK
- #ifdef MEMORYPOOL_DEBUG
- /**
- if you define MEMORYPOOL_INTENSE_VERIFY, we do intensive verifications after
- nearly every memory operation. this is OFF by default, since it slows down
- things a lot, but is worth turning on for really obscure memory corruption issues.
- */
- #ifndef MEMORYPOOL_INTENSE_VERIFY
- #define NO_MEMORYPOOL_INTENSE_VERIFY
- #endif
- /**
- if you define MEMORYPOOL_CHECK_BLOCK_OWNERSHIP, we do lots of calls to verify
- that a block actually belongs to the pool it is called with. this is great
- for debugging, but can be realllly slow, so is off by default.
- */
- #ifndef MEMORYPOOL_CHECK_BLOCK_OWNERSHIP
- #define NO_MEMORYPOOL_CHECK_BLOCK_OWNERSHIP
- #endif
- static const char* FREE_SINGLEBLOCK_TAG_STRING = "FREE_SINGLEBLOCK_TAG_STRING";
- const Short SINGLEBLOCK_MAGIC_COOKIE = 12345;
- const Int GARBAGE_FILL_VALUE = 0xdeadbeef;
- // flags for m_debugFlags
- enum
- {
- IGNORE_LEAKS = 0x0001
- };
- // in debug mode (but not internal), save stacktraces too
- #if !defined(MEMORYPOOL_CHECKPOINTING) && defined(MEMORYPOOL_STACKTRACE) && defined(_DEBUG)
- #define MEMORYPOOL_SINGLEBLOCK_GETS_STACKTRACE
- #endif
- #define USE_FILLER_VALUE
- const Int MAX_INIT_FILLER_COUNT = 8;
- #ifdef USE_FILLER_VALUE
- static UnsignedInt s_initFillerValue = 0xf00dcafe; // will be replaced, should never be this value at runtime
- static void calcFillerValue(Int index)
- {
- s_initFillerValue = (index & 3) << 1;
- s_initFillerValue |= 0x01;
- s_initFillerValue |= (~(s_initFillerValue << 4)) & 0xf0;
- s_initFillerValue |= (s_initFillerValue << 8);
- s_initFillerValue |= (s_initFillerValue << 16);
- DEBUG_LOG(("Setting MemoryPool initFillerValue to %08x (index %d)\n",s_initFillerValue,index));
- }
- #endif
- #endif
- #ifdef MEMORYPOOL_BOUNDINGWALL
- #define WALLCOUNT (2) // default setting of 8 requires 4*4*2==32 extra bytes PER BLOCK
- #define WALLSIZE (WALLCOUNT * sizeof(Int))
- #endif
- #ifdef MEMORYPOOL_STACKTRACE
- #define MEMORYPOOL_STACKTRACE_SIZE (20)
- #define MEMORYPOOL_STACKTRACE_SKIP_SIZE (6)
- #define MEMORYPOOL_STACKTRACE_SIZE_BYTES (MEMORYPOOL_STACKTRACE_SIZE * sizeof(void*))
- #endif
- // ----------------------------------------------------------------------------
- // PRIVATE DATA
- // ----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_BOUNDINGWALL
- static Int theBoundingWallPattern = 0xbabeface;
- #endif
- #ifdef MEMORYPOOL_STACKTRACE
- /** the max number levels to dump in the stacktrace. a variable rather than
- constant so that you can fiddle with it in the debugger if desired, to
- get shorter or longer dumps. (you can't go longer than MEMORYPOOL_STACKTRACE_SIZE
- in any event. */
- static Int theStackTraceDepth = 16;
- #endif
- #ifdef MEMORYPOOL_DEBUG
- static Int theTotalSystemAllocationInBytes = 0;
- static Int thePeakSystemAllocationInBytes = 0;
- static Int theTotalLargeBlocks = 0;
- static Int thePeakLargeBlocks = 0;
- Int theTotalDMA = 0;
- Int thePeakDMA = 0;
- Int theWastedDMA = 0;
- Int thePeakWastedDMA = 0;
- #define NO_INTENSE_DMA_BOOKKEEPING
- #ifdef INTENSE_DMA_BOOKKEEPING
- struct UsedNPeak
- {
- Int used, peak, waste, peakwaste;
- UsedNPeak() : used(0), peak(0), waste(0), peakwaste(0) { }
- };
- typedef std::map< const char*, UsedNPeak, std::less<const char*> > UsedNPeakMap;
- static UsedNPeakMap TheUsedNPeakMap;
- static Int doingIntenseDMA = 0;
- #endif
- #endif
- static Bool thePreMainInitFlag = false;
- static Bool theMainInitFlag = false;
- // ----------------------------------------------------------------------------
- // PRIVATE PROTOTYPES
- // ----------------------------------------------------------------------------
- /// @todo srj -- make this work for 8
- #define MEM_BOUND_ALIGNMENT 4
- static Int roundUpMemBound(Int i);
- static void *sysAllocate(Int numBytes);
- static void *sysAllocateDoNotZero(Int numBytes);
- static void sysFree(void* p);
- static void memset32(void* ptr, Int value, Int bytesToFill);
- #ifdef MEMORYPOOL_STACKTRACE
- static void doStackDumpOutput(const char* m);
- static void doStackDump(void **stacktrace, int size);
- #endif
- static void preMainInitMemoryManager();
- // ----------------------------------------------------------------------------
- // PRIVATE FUNCTIONS
- // ----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- /** round up to the nearest multiple of MEM_BOUND_ALIGNMENT */
- static Int roundUpMemBound(Int i)
- {
- return (i + (MEM_BOUND_ALIGNMENT-1)) & ~(MEM_BOUND_ALIGNMENT-1);
- }
- //-----------------------------------------------------------------------------
- /**
- identical to sysAllocateDoNotZero, except that the memory block returned
- is filled to all-zero-bytes.
- */
- static void* sysAllocate(Int numBytes)
- {
- void* p = ::GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, numBytes);
- if (!p)
- throw ERROR_OUT_OF_MEMORY;
- #ifdef MEMORYPOOL_DEBUG
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- theTotalSystemAllocationInBytes += ::GlobalSize(p);
- if (thePeakSystemAllocationInBytes < theTotalSystemAllocationInBytes)
- thePeakSystemAllocationInBytes = theTotalSystemAllocationInBytes;
- }
- #endif
- return p;
- }
- //-----------------------------------------------------------------------------
- /**
- this is the low-level allocator that we use to request memory from the OS.
- all (repeat, all) memory allocations in this module should ultimately
- go thru this routine (or sysAllocate).
- note: throws ERROR_OUT_OF_MEMORY on failure; never returns null
- */
- static void* sysAllocateDoNotZero(Int numBytes)
- {
- void* p = ::GlobalAlloc(GMEM_FIXED, numBytes);
- if (!p)
- throw ERROR_OUT_OF_MEMORY;
- #ifdef MEMORYPOOL_DEBUG
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- #ifdef USE_FILLER_VALUE
- {
- USE_PERF_TIMER(MemoryPoolInitFilling)
- ::memset32(p, s_initFillerValue, ::GlobalSize(p));
- }
- #endif
- theTotalSystemAllocationInBytes += ::GlobalSize(p);
- if (thePeakSystemAllocationInBytes < theTotalSystemAllocationInBytes)
- thePeakSystemAllocationInBytes = theTotalSystemAllocationInBytes;
- }
- #endif
- return p;
- }
- //-----------------------------------------------------------------------------
- /**
- the counterpart to sysAllocate / sysAllocateDoNotZero; used to free blocks
- allocated by them. it is OK to pass null here (it will just be ignored).
- */
- static void sysFree(void* p)
- {
- if (p)
- {
- #ifdef MEMORYPOOL_DEBUG
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- ::memset32(p, GARBAGE_FILL_VALUE, ::GlobalSize(p));
- theTotalSystemAllocationInBytes -= ::GlobalSize(p);
- }
- #endif
- ::GlobalFree(p);
- }
- }
- // ----------------------------------------------------------------------------
- /**
- fills memory with a 32-bit value (note: assumes the ptr is 4-byte-aligned)
- */
- static void memset32(void* ptr, Int value, Int bytesToFill)
- {
- Int wordsToFill = bytesToFill>>2;
- bytesToFill -= (wordsToFill<<2);
- Int *p = (Int*)ptr;
- for (++wordsToFill; --wordsToFill; )
- *p++ = value;
- Byte *b = (Byte *)p;
- for (++bytesToFill; --bytesToFill; )
- *b++ = (Byte)value;
- }
- #ifdef MEMORYPOOL_STACKTRACE
- // ----------------------------------------------------------------------------
- /**
- This is just a convenience routine that dumps output from the StackDump module
- to our normal debug log file, with a little massaging for formatting.
- */
- static void doStackDumpOutput(const char* m)
- {
- const char *PREPEND = "STACKTRACE";
- if (*m == 0 || strcmp(m, "\n") == 0)
- {
- DEBUG_LOG((m));
- }
- else
- {
- // Note - I am moving the prepend to the end, as this allows double clicking in the
- // output window to open the file in VisualStudio. jba.
- DEBUG_LOG(("%s, %s",m, PREPEND));
- }
- }
- #endif
- #ifdef MEMORYPOOL_STACKTRACE
- // ----------------------------------------------------------------------------
- /**
- dump the given stacktrace to the debug log.
- */
- static void doStackDump(void **stacktrace, int size)
- {
- ::doStackDumpOutput("Allocation Stack Trace:");
- ::doStackDumpOutput("\n");
- ::StackDumpFromAddresses(stacktrace, size, ::doStackDumpOutput);
- }
- #endif
- // ----------------------------------------------------------------------------
- // PRIVATE TYPES
- // ----------------------------------------------------------------------------
- // ----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- This is a auxiliary record that we allocate in debug modes (actually, checkpoint modes)
- to retain extra information about blocks; there is a one-to-one correspondence
- between this record and a block allocation. The interesting bit about this record is that
- we don't deallocate it when the corresponding block is freed; we retain it so we can
- later provide information about what blocks were freed when, etc. Yes, this does chew
- up a lot of memory! That's why it's debug-mode only; the presumption is that developers
- machines have boatloads of RAM. (Note that we *do* free these when resetCheckpoints() is called.)
- Note also that we directly allocate/free these with sysAllocate/sysFree, so ctors/dtors
- are never executed, nor would virtual functions work -- I know, it's a little evil.
- */
- class BlockCheckpointInfo
- {
- private:
- BlockCheckpointInfo *m_next; ///< next checkpoint in this pool/dma
- const char *m_debugLiteralTagString; ///< the tagstring for the block
- Int m_allocCheckpoint; ///< when it was allocated
- Int m_freeCheckpoint; ///< when it was freed (-1 if still in existence)
- Int m_blockSize; ///< logical size of the block
- #ifdef MEMORYPOOL_STACKTRACE
- void* m_stacktrace[MEMORYPOOL_STACKTRACE_SIZE]; ///< stacktrace of when block was allocated
- #endif
- ~BlockCheckpointInfo() {};
- public:
- BlockCheckpointInfo *getNext(); ///< return next checkpointinfo for this pool/dma
- void debugSetFreepoint(Int f); ///< set the checkpoint at which the block was freed.
- #ifdef MEMORYPOOL_STACKTRACE
- void **getStacktraceInfo(); ///< return a ptr to the allocation stacktrace info.
- #endif
-
- static BlockCheckpointInfo *addToList(
- BlockCheckpointInfo **pHead,
- const char *debugLiteralTagString,
- Int allocCheckpoint,
- Int blockSize
- );
-
- static void freeList(BlockCheckpointInfo **pHead);
- Bool shouldBeInReport(Int flags, Int startCheckpoint, Int endCheckpoint);
- static void doBlockCheckpointReport( BlockCheckpointInfo *bi, const char *poolName,
- Int flags, Int startCheckpoint, Int endCheckpoint );
- };
- #endif
- // ----------------------------------------------------------------------------
- /**
- This is the fundamental allocation unit; when you allocate via (say) MemoryPool::allocateBlock,
- this is what is being allocated for you. (Of course, you don't see the private fields.)
- For the most part, we allocate big chunks of these in a monolithic Blob and subdivide
- from there. (However, we occasionally allocate these individually, for large blocks.)
- Note also that we directly allocate/free these with sysAllocate/sysFree, so ctors/dtors
- are never executed, nor would virtual functions work -- I know, it's a little evil.
- */
- class MemoryPoolSingleBlock
- {
- private:
- MemoryPoolBlob *m_owningBlob; ///< will be NULL if the single block was allocated via sysAllocate()
- MemoryPoolSingleBlock *m_nextBlock; ///< if m_owningBlob is nonnull, this points to next free (unallocated) block in the blob; if m_owningBlob is null, this points to the next used (allocated) raw block in the pool.
- #ifdef MPSB_DLINK
- MemoryPoolSingleBlock *m_prevBlock; ///< if m_owningBlob is nonnull, this points to prev free (unallocated) block in the blob; if m_owningBlob is null, this points to the prev used (allocated) raw block in the pool.
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- BlockCheckpointInfo *m_checkpointInfo; ///< ptr to the checkpointinfo for this block
- #endif
- #ifdef MEMORYPOOL_BOUNDINGWALL
- Int m_wallPattern; ///< unique seed value for the bounding-walls for this block
- #endif
- #ifdef MEMORYPOOL_DEBUG
- const char *m_debugLiteralTagString; ///< ptr to the tagstring for this block.
- Int m_logicalSize; ///< logical size of block (not including overhead, walls, etc.)
- Int m_wastedSize; ///< if allocated via DMA, the "wasted" bytes
- Short m_magicCookie; ///< magic value used to verify that the block is one of ours (as opposed to random pointer)
- Short m_debugFlags; ///< misc flags
- #ifdef MEMORYPOOL_SINGLEBLOCK_GETS_STACKTRACE
- void* m_stacktrace[MEMORYPOOL_STACKTRACE_SIZE]; ///< stacktrace of when block was allocated (if not checkpointing)
- #endif
- #endif
- private:
- void* getUserDataNoDbg();
- #ifdef MEMORYPOOL_BOUNDINGWALL
- void debugFillInWalls();
- #endif
- public:
-
- static Int calcRawBlockSize(Int logicalSize);
- static MemoryPoolSingleBlock *rawAllocateSingleBlock(MemoryPoolSingleBlock **pRawListHead, Int logicalSize, MemoryPoolFactory *owningFactory DECLARE_LITERALSTRING_ARG2);
- void removeBlockFromList(MemoryPoolSingleBlock **pHead);
- void initBlock(Int logicalSize, MemoryPoolBlob *owningBlob, MemoryPoolFactory *owningFactory DECLARE_LITERALSTRING_ARG2);
- void* getUserData();
- static MemoryPoolSingleBlock *recoverBlockFromUserData(void* pUserData);
- MemoryPoolBlob *getOwningBlob();
- MemoryPoolSingleBlock *getNextFreeBlock();
- void setNextFreeBlock(MemoryPoolSingleBlock *b);
- MemoryPoolSingleBlock *getNextRawBlock();
- void setNextRawBlock(MemoryPoolSingleBlock *b);
- #ifdef MEMORYPOOL_DEBUG
- void debugIgnoreLeaksForThisBlock();
- const char *debugGetLiteralTagString();
- Int debugGetLogicalSize();
- Int debugGetWastedSize();
- void debugSetWastedSize(Int waste);
- void debugVerifyBlock();
- void debugMarkBlockAsFree();
- Bool debugCheckUnderrun();
- Bool debugCheckOverrun();
- Int debugSingleBlockReportLeak(const char* owner);
- #endif // MEMORYPOOL_DEBUG
- #ifdef MEMORYPOOL_CHECKPOINTING
- BlockCheckpointInfo *debugGetCheckpointInfo();
- void debugSetCheckpointInfo(BlockCheckpointInfo *bi);
- void debugResetCheckpoint();
- #endif
- };
- // ----------------------------------------------------------------------------
- class MemoryPoolBlob
- {
- private:
- MemoryPool *m_owningPool; ///< the pool that owns this blob
- MemoryPoolBlob *m_nextBlob; ///< next blob in this pool
- MemoryPoolBlob *m_prevBlob; ///< prev blob in this pool
- MemoryPoolSingleBlock *m_firstFreeBlock; ///< ptr to first available block in this blob
- Int m_usedBlocksInBlob; ///< total allocated blocks in this blob
- Int m_totalBlocksInBlob; ///< total blocks in this blob (allocated + available)
- char *m_blockData; ///< ptr to the blocks (really a MemoryPoolSingleBlock*)
- public:
- MemoryPoolBlob();
- ~MemoryPoolBlob();
- void initBlob(MemoryPool *owningPool, Int allocationCount);
- void addBlobToList(MemoryPoolBlob **ppHead, MemoryPoolBlob **ppTail);
- void removeBlobFromList(MemoryPoolBlob **ppHead, MemoryPoolBlob **ppTail);
- MemoryPoolBlob *getNextInList();
- Bool hasAnyFreeBlocks();
- MemoryPoolSingleBlock *allocateSingleBlock(DECLARE_LITERALSTRING_ARG1);
- void freeSingleBlock(MemoryPoolSingleBlock *block);
- MemoryPool *getOwningPool();
- Int getFreeBlockCount();
- Int getUsedBlockCount();
- Int getTotalBlockCount();
- #ifdef MEMORYPOOL_DEBUG
- void debugMemoryVerifyBlob();
- Int debugBlobReportLeaks(const char* owner);
- Bool debugIsBlockInBlob(void *pBlock);
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- void debugResetCheckpoints();
- #endif
- };
- // ----------------------------------------------------------------------------
- // PUBLIC DATA
- // ----------------------------------------------------------------------------
- MemoryPoolFactory *TheMemoryPoolFactory = NULL;
- DynamicMemoryAllocator *TheDynamicMemoryAllocator = NULL;
- // ----------------------------------------------------------------------------
- // INLINES
- // ----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- inline BlockCheckpointInfo *BlockCheckpointInfo::getNext() { return m_next; }
- inline void BlockCheckpointInfo::debugSetFreepoint(Int f) { DEBUG_ASSERTCRASH(m_freeCheckpoint == -1, ("already have a freepoint")); m_freeCheckpoint = f; }
- #ifdef MEMORYPOOL_STACKTRACE
- inline void **BlockCheckpointInfo::getStacktraceInfo() { return m_stacktrace; }
- #endif
- #endif
- // ----------------------------------------------------------------------------
- /**
- return a ptr to the user-data area of the block (ie, the part the enduser can deal with).
- this call does NO debug verification and is for internal use of class MemoryPoolSingleBlock only.
- */
- inline void* MemoryPoolSingleBlock::getUserDataNoDbg()
- {
- char* p = ((char*)this) + sizeof(MemoryPoolSingleBlock);
- #ifdef MEMORYPOOL_BOUNDINGWALL
- p += WALLSIZE;
- #endif
- return (void*)p;
- }
- /**
- return a ptr to the user-data area of the block (ie, the part the enduser can deal with).
- this call verifies that the block is valid in debug mode.
- */
- inline void* MemoryPoolSingleBlock::getUserData()
- {
- // yes, verify the block in this case for plain debug mode (not intense-verify mode)
- #ifdef MEMORYPOOL_DEBUG
- debugVerifyBlock();
- #endif
- return getUserDataNoDbg();
- }
- /**
- given a desired logical block size, calculate the physical size needed for each
- MemoryPoolSingleBlock (including overhead, etc.)
- */
- inline /*static*/ Int MemoryPoolSingleBlock::calcRawBlockSize(Int logicalSize)
- {
- Int s = ::roundUpMemBound(logicalSize) + sizeof(MemoryPoolSingleBlock);
- #ifdef MEMORYPOOL_BOUNDINGWALL
- s += WALLSIZE*2;
- #endif
- return s;
- }
- /**
- accessor
- */
- inline MemoryPoolBlob *MemoryPoolSingleBlock::getOwningBlob()
- {
- return m_owningBlob;
- }
- /**
- return the next free block in this blob. this call assumes that the block
- in question belongs to a blob, and will assert if not.
- */
- inline MemoryPoolSingleBlock *MemoryPoolSingleBlock::getNextFreeBlock()
- {
- DEBUG_ASSERTCRASH(m_owningBlob != NULL, ("must be called on blob block"));
- return m_nextBlock;
- }
- /**
- set the next free block in this blob. this call assumes that both blocks
- in question belongs to a blob, but will NOT assert if not, since it may be
- called when the blocks are in an inconsistent state.
- */
- inline void MemoryPoolSingleBlock::setNextFreeBlock(MemoryPoolSingleBlock *b)
- {
- //DEBUG_ASSERTCRASH(m_owningBlob != NULL && b->m_owningBlob != NULL, ("must be called on blob block"));
- // don't check the 'b' block -- we need to call this before 'b' is fully initialized.
- DEBUG_ASSERTCRASH(m_owningBlob != NULL, ("must be called on blob block"));
- this->m_nextBlock = b;
- #ifdef MPSB_DLINK
- if (b) {
- b->m_prevBlock = this;
- }
- #endif
- }
- /**
- return the next raw block in this dma. this call assumes that the block
- in question does NOT belong to a blob, and will assert if not.
- */
- inline MemoryPoolSingleBlock *MemoryPoolSingleBlock::getNextRawBlock()
- {
- DEBUG_ASSERTCRASH(m_owningBlob == NULL, ("must be called on raw block"));
- return m_nextBlock;
- }
- /**
- set the next raw block in this dma. this call assumes that the blocks
- in question do NOT belong to a blob, and will assert if not.
- */
- inline void MemoryPoolSingleBlock::setNextRawBlock(MemoryPoolSingleBlock *b)
- {
- DEBUG_ASSERTCRASH(m_owningBlob == NULL && (!b || b->m_owningBlob == NULL), ("must be called on raw block"));
- m_nextBlock = b;
- #ifdef MPSB_DLINK
- if (b)
- b->m_prevBlock = this;
- #endif
- }
- #ifdef MEMORYPOOL_DEBUG
- inline void MemoryPoolSingleBlock::debugIgnoreLeaksForThisBlock()
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) not worth it
- m_debugFlags |= IGNORE_LEAKS;
- }
- /**
- accessor
- */
- inline const char *MemoryPoolSingleBlock::debugGetLiteralTagString()
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) not worth it
- return m_debugLiteralTagString;
- }
- #endif
- #ifdef MEMORYPOOL_DEBUG
- /**
- accessor
- */
- inline Int MemoryPoolSingleBlock::debugGetLogicalSize()
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) not worth it
- return m_logicalSize;
- }
- #endif
- #ifdef MEMORYPOOL_DEBUG
- /**
- accessor
- */
- inline Int MemoryPoolSingleBlock::debugGetWastedSize()
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) not worth it
- return m_wastedSize;
- }
- #endif
- #ifdef MEMORYPOOL_DEBUG
- inline void MemoryPoolSingleBlock::debugSetWastedSize(Int w)
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) not worth it
- m_wastedSize = w;
- }
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- accessor
- */
- inline BlockCheckpointInfo *MemoryPoolSingleBlock::debugGetCheckpointInfo()
- {
- return m_checkpointInfo;
- }
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- set the checkpoint info for this block.
- */
- inline void MemoryPoolSingleBlock::debugSetCheckpointInfo(BlockCheckpointInfo *bi)
- {
- DEBUG_ASSERTCRASH(m_checkpointInfo == NULL, ("should be null"));
- m_checkpointInfo = bi;
- }
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- sets the checkpointinfo to null, but does NOT free it... the checkpointinfo
- is expected to be freed elsewhere.
- */
- inline void MemoryPoolSingleBlock::debugResetCheckpoint()
- {
- m_checkpointInfo = NULL;
- }
- #endif
- // ----------------------------------------------------------------------------
- /// accessor
- inline MemoryPoolBlob *MemoryPoolBlob::getNextInList() { return m_nextBlob; }
- /// accessor
- inline Bool MemoryPoolBlob::hasAnyFreeBlocks() { return m_firstFreeBlock != NULL; }
- /// accessor
- inline MemoryPool *MemoryPoolBlob::getOwningPool() { return m_owningPool; }
- /// accessor
- inline Int MemoryPoolBlob::getFreeBlockCount() { return getTotalBlockCount() - getUsedBlockCount(); }
- /// accessor
- inline Int MemoryPoolBlob::getUsedBlockCount() { return m_usedBlocksInBlob; }
- /// accessor
- inline Int MemoryPoolBlob::getTotalBlockCount() { return m_totalBlocksInBlob; }
- //-----------------------------------------------------------------------------
- // METHODS for BlockCheckpointInfo
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /// return true iff this checkpointinfo should be included in a checkpointreport with the given parameters.
- Bool BlockCheckpointInfo::shouldBeInReport(Int flags, Int startCheckpoint, Int endCheckpoint)
- {
- Bool allocFlagsOK = false;
- Bool freedFlagsOK = false;
- if (m_allocCheckpoint < startCheckpoint && (flags & _REPORT_CP_ALLOCATED_BEFORE))
- allocFlagsOK = true;
- if (m_allocCheckpoint >= startCheckpoint && m_allocCheckpoint < endCheckpoint && (flags & _REPORT_CP_ALLOCATED_BETWEEN))
- allocFlagsOK = true;
- if (m_freeCheckpoint == -1)
- {
- // block still exists! process this only if we want 'em.
- freedFlagsOK = (flags & _REPORT_CP_FREED_NEVER) ? true : false;
- }
- else
- {
- if (m_freeCheckpoint < startCheckpoint && (flags & _REPORT_CP_FREED_BEFORE))
- freedFlagsOK = true;
- if (m_freeCheckpoint >= startCheckpoint && m_freeCheckpoint < endCheckpoint && (flags & _REPORT_CP_FREED_BETWEEN))
- freedFlagsOK = true;
- }
- // the block must match both the 'alloc' and 'free' criteria to get a report
- return allocFlagsOK && freedFlagsOK;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /// print a checkpointreport for the given checkpointinfo. if checkpointinfo is null, print column headers.
- /*static*/ void BlockCheckpointInfo::doBlockCheckpointReport(BlockCheckpointInfo *bi,
- const char *poolName, Int flags, Int startCheckpoint, Int endCheckpoint )
- {
- const char *PREPEND = "BLOCKINFO"; // allows grepping more easily
- if (!bi)
- {
- DEBUG_LOG(("%s,%32s,%6s,%6s,%6s,%s\n",PREPEND,"POOLNAME","BLKSZ","ALLOC","FREED","BLOCKNAME"));
- }
- else
- {
- DEBUG_ASSERTCRASH(startCheckpoint >= 0 && startCheckpoint <= endCheckpoint, ("bad checkpoints"));
- DEBUG_ASSERTCRASH((flags & _REPORT_CP_ALLOCATED_DONTCARE) != 0, ("bad flags: must set at least one alloc flag"));
- DEBUG_ASSERTCRASH((flags & _REPORT_CP_FREED_DONTCARE) != 0, ("bad flags: must set at least one freed flag"));
- if (bi->shouldBeInReport(flags, startCheckpoint, endCheckpoint))
- {
- DEBUG_LOG(("%s,%32s,%6d,%6d,%6d,%s\n",PREPEND,poolName,bi->m_blockSize,bi->m_allocCheckpoint,bi->m_freeCheckpoint,bi->m_debugLiteralTagString));
- #ifdef MEMORYPOOL_STACKTRACE
- if (flags & REPORT_CP_STACKTRACE)
- {
- ::doStackDump(bi->m_stacktrace, min(MEMORYPOOL_STACKTRACE_SIZE, theStackTraceDepth ));
- }
- #endif
- }
- }
- }
- #endif
- // ----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /// free an entire list of checkpointinfos.
- /*static*/ void BlockCheckpointInfo::freeList(BlockCheckpointInfo **pHead)
- {
- BlockCheckpointInfo *p = *pHead;
- while (p)
- {
- BlockCheckpointInfo *n = p->m_next;
- ::sysFree((void *)p);
- p = n;
- }
- *pHead = NULL;
- }
- #endif
- // ----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- allocate a new checkpointinfo with the given tag/checkpoint/size, add it to the
- linked list, and return the checkpointinfo. (note that this will NOT throw an exception;
- if there is not enough memory to allocate a new checkpointinfo, it will quietly return null.)
- */
- /*static*/ BlockCheckpointInfo *BlockCheckpointInfo::addToList(
- BlockCheckpointInfo **pHead,
- const char *debugLiteralTagString,
- Int allocCheckpoint,
- Int blockSize
- )
- {
- DEBUG_ASSERTCRASH(debugLiteralTagString != FREE_SINGLEBLOCK_TAG_STRING, ("bad tag string"));
- BlockCheckpointInfo *freed = NULL;
- try {
- freed = (BlockCheckpointInfo *)::sysAllocate(sizeof(BlockCheckpointInfo));
- } catch (...) {
- freed = NULL;
- }
- if (freed)
- {
- DEBUG_ASSERTCRASH(debugLiteralTagString != NULL, ("null tagstrings are not allowed"));
- freed->m_debugLiteralTagString = debugLiteralTagString;
- freed->m_allocCheckpoint = allocCheckpoint;
- freed->m_freeCheckpoint = -1;
- freed->m_blockSize = blockSize;
- freed->m_next = *pHead;
- *pHead = freed;
- }
- return freed;
- }
- #endif
- //-----------------------------------------------------------------------------
- // METHODS for MemoryPoolSingleBlock
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- /**
- fill in a block's fields. this is usually done only just after a block is allocated,
- but might also be done at other points in debug mode.
- */
- void MemoryPoolSingleBlock::initBlock(Int logicalSize, MemoryPoolBlob *owningBlob,
- MemoryPoolFactory *owningFactory DECLARE_LITERALSTRING_ARG2)
- {
- // Note that while it is OK for owningBlob to be null, it is NEVER ok
- // for owningFactory to be null.
- DEBUG_ASSERTCRASH(owningFactory, ("null factory"));
- #ifdef MEMORYPOOL_DEBUG
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- m_magicCookie = SINGLEBLOCK_MAGIC_COOKIE;
- m_debugFlags = 0;
- if (!theMainInitFlag)
- debugIgnoreLeaksForThisBlock();
- DEBUG_ASSERTCRASH(debugLiteralTagString != NULL, ("null tagstrings are not allowed"));
- m_debugLiteralTagString = debugLiteralTagString;
- m_logicalSize = logicalSize;
- m_wastedSize = 0;
- #ifdef MEMORYPOOL_SINGLEBLOCK_GETS_STACKTRACE
- if (theStackTraceDepth > 0 && (!TheGlobalData || TheGlobalData->m_checkForLeaks))
- {
- memset(m_stacktrace, 0, MEMORYPOOL_STACKTRACE_SIZE_BYTES);
- ::FillStackAddresses(m_stacktrace, min(MEMORYPOOL_STACKTRACE_SIZE, theStackTraceDepth), MEMORYPOOL_STACKTRACE_SKIP_SIZE);
- }
- else
- {
- m_stacktrace[0] = NULL;
- }
- #endif
- }
- #endif MEMORYPOOL_DEBUG
- #ifdef MEMORYPOOL_CHECKPOINTING
- m_checkpointInfo = NULL;
- #endif
- #ifdef MEMORYPOOL_BOUNDINGWALL
- m_wallPattern = theBoundingWallPattern++;
- debugFillInWalls();
- #endif
- m_nextBlock = NULL;
- #ifdef MPSB_DLINK
- m_prevBlock = NULL;
- #endif
- m_owningBlob = owningBlob; // could be NULL
- }
- //-----------------------------------------------------------------------------
- /**
- given a 'public' ptr to a block (ie, the ptr returned by the MemoryPool::allocateBlock),
- recover the ptr to the MemoryPoolSingleBlock, so we can access the hidden fields.
- */
- /* static */ MemoryPoolSingleBlock *MemoryPoolSingleBlock::recoverBlockFromUserData(void* pUserData)
- {
- DEBUG_ASSERTCRASH(pUserData, ("null pUserData"));
- if (!pUserData)
- return NULL;
- char* p = ((char*)pUserData) - sizeof(MemoryPoolSingleBlock);
- #ifdef MEMORYPOOL_BOUNDINGWALL
- p -= WALLSIZE;
- #endif
- MemoryPoolSingleBlock *block = (MemoryPoolSingleBlock *)p;
- // yes, verify the block in this case for plain debug mode (not intense-verify mode)
- #ifdef MEMORYPOOL_DEBUG
- block->debugVerifyBlock();
- #endif
- return block;
- }
- //-----------------------------------------------------------------------------
- /**
- allocate and initialize a single block. this should only used by DynamicMemoryAllocator
- when allocating an extraordinarily large block.
- */
- /*static*/ MemoryPoolSingleBlock *MemoryPoolSingleBlock::rawAllocateSingleBlock(
- MemoryPoolSingleBlock **pRawListHead,
- Int logicalSize,
- MemoryPoolFactory *owningFactory
- DECLARE_LITERALSTRING_ARG2)
- {
- MemoryPoolSingleBlock *block = (MemoryPoolSingleBlock *)::sysAllocateDoNotZero(calcRawBlockSize(logicalSize));
- block->initBlock(logicalSize, NULL, owningFactory PASS_LITERALSTRING_ARG2);
- block->setNextRawBlock(*pRawListHead);
- *pRawListHead = block;
- return block;
- }
- //-----------------------------------------------------------------------------
- /**
- remove the block from the list, which is presumed to be a list of raw blocks.
- generally, only the DynamicMemoryAllocator should call this.
- */
- void MemoryPoolSingleBlock::removeBlockFromList(MemoryPoolSingleBlock **pHead)
- {
- DEBUG_ASSERTCRASH(this->m_owningBlob == NULL, ("this function should only be used on raw blocks"));
- #ifdef MPSB_DLINK
- DEBUG_ASSERTCRASH(this->m_nextBlock == NULL || this->m_nextBlock->m_owningBlob == NULL, ("this function should only be used on raw blocks"));
- if (this->m_prevBlock)
- {
- DEBUG_ASSERTCRASH(this->m_prevBlock->m_owningBlob == NULL, ("this function should only be used on raw blocks"));
- DEBUG_ASSERTCRASH(*pHead != this, ("bad linkage"));
- this->m_prevBlock->m_nextBlock = this->m_nextBlock;
- }
- else
- {
- DEBUG_ASSERTCRASH(*pHead == this, ("bad linkage"));
- *pHead = this->m_nextBlock;
- }
- if (this->m_nextBlock)
- {
- DEBUG_ASSERTCRASH(this->m_nextBlock->m_owningBlob == NULL, ("this function should only be used on raw blocks"));
- this->m_nextBlock->m_prevBlock = this->m_prevBlock;
- }
- #else
- // this isn't very efficient, and may need upgrading... but to do so
- // would require adding a back link, so I'd rather do some testing
- // first to see if it's really a speed issue in practice. (the only place
- // this is used is when freeing 'raw' blocks allocated via the DMA).
- MemoryPoolSingleBlock *prev = NULL;
- for (MemoryPoolSingleBlock *cur = *pHead; cur; cur = cur->m_nextBlock)
- {
- DEBUG_ASSERTCRASH(cur->m_owningBlob == NULL, ("this function should only be used on raw blocks"));
- if (cur == this)
- {
- if (prev)
- {
- prev->m_nextBlock = this->m_nextBlock;
- }
- else
- {
- *pHead = this->m_nextBlock;
- }
- break;
- }
- prev = cur;
- }
- #endif
- }
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- Int MemoryPoolSingleBlock::debugSingleBlockReportLeak(const char* owner)
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) skip end-of-run reporting stuff
- // if allocated before main... just ignore the leak.
- if (m_debugFlags & IGNORE_LEAKS)
- return 0;
- // it's free, ergo, not leaked.
- if (m_debugLiteralTagString == FREE_SINGLEBLOCK_TAG_STRING)
- return 0;
- if (strcmp(m_debugLiteralTagString, "STR_AsciiString::ensureUniqueBufferOfSize") == 0)
- {
- /** @todo srj -- we leak a bunch of these for some reason (probably due to leaking Win32LocalFile)
- so just ignore 'em for now... figure out later. */
- }
- else if (strstr(m_debugLiteralTagString, "Win32LocalFileSystem.cpp") != NULL)
- {
- /** @todo srj -- we leak a bunch of these for some reason
- so just ignore 'em for now... figure out later. */
- }
- else
- {
- DEBUG_LOG(("Leaked a block of size %d, tagstring %s, from pool/dma %s\n",m_logicalSize,m_debugLiteralTagString,owner));
- }
- #ifdef MEMORYPOOL_SINGLEBLOCK_GETS_STACKTRACE
- if (!TheGlobalData || TheGlobalData->m_checkForLeaks)
- ::doStackDump(m_stacktrace, min(MEMORYPOOL_STACKTRACE_SIZE, theStackTraceDepth));
- #endif
- return 1;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- Verify internal consistency of this block.
- */
- void MemoryPoolSingleBlock::debugVerifyBlock()
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- DEBUG_ASSERTCRASH(this, ("null this"));
- DEBUG_ASSERTCRASH(m_magicCookie == SINGLEBLOCK_MAGIC_COOKIE, ("wrong cookie"));
- DEBUG_ASSERTCRASH(m_debugLiteralTagString != NULL, ("bad tagstring"));
- /// @todo Put this check back in after the AI memory usage is under control (MSB)
- //DEBUG_ASSERTCRASH(m_logicalSize>0 && m_logicalSize < 0x00ffffff, ("unlikely value for m_logicalSize"));
- DEBUG_ASSERTCRASH(!m_nextBlock || m_nextBlock->m_owningBlob == m_owningBlob, ("owning blob mismatch..."));
- #ifdef MPSB_DLINK
- DEBUG_ASSERTCRASH(!m_prevBlock || m_prevBlock->m_owningBlob == m_owningBlob, ("owning blob mismatch..."));
- #endif
- debugCheckUnderrun();
- debugCheckOverrun();
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- Fill block with bogus values and mark other internal fields for debugging purposes.
- */
- void MemoryPoolSingleBlock::debugMarkBlockAsFree()
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- ::memset32(getUserDataNoDbg(), GARBAGE_FILL_VALUE, m_logicalSize);
- m_debugLiteralTagString = FREE_SINGLEBLOCK_TAG_STRING;
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugVerifyBlock();
- #endif
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- Returns true iff someone overwrote part of the first bounding wall
- (ie, tromped on memory just before the block)
- */
- Bool MemoryPoolSingleBlock::debugCheckUnderrun()
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- #ifdef MEMORYPOOL_BOUNDINGWALL
- Int *p = (Int*)(((char*)getUserDataNoDbg()) - WALLSIZE);
- for (Int i = 0; i < WALLCOUNT; i++, p++)
- {
- if (*p != m_wallPattern+i)
- {
- DEBUG_CRASH(("memory underrun for block \"%s\" (expected %08x, got %08x)\n",m_debugLiteralTagString,m_wallPattern+i,*p));
- return true;
- }
- }
- #endif
- return false;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- Returns true iff someone overwrote part of the second bounding wall
- (ie, tromped on memory just after the block)
- */
- Bool MemoryPoolSingleBlock::debugCheckOverrun()
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- #ifdef MEMORYPOOL_BOUNDINGWALL
- Int *p = (Int*)(((char*)getUserDataNoDbg()) + m_logicalSize);
- for (Int i = 0; i < WALLCOUNT; i++, p++)
- {
- if (*p != m_wallPattern-i)
- {
- DEBUG_CRASH(("memory overrun for block \"%s\" (expected %08x, got %08x)\n",m_debugLiteralTagString,m_wallPattern+i,*p));
- return true;
- }
- }
- #endif
- return false;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_BOUNDINGWALL
- /**
- Fill in the proper values for this block's bounding walls.
- */
- void MemoryPoolSingleBlock::debugFillInWalls()
- {
- Int *p;
- Int i;
- p = (Int*)(((char*)getUserDataNoDbg()) - WALLSIZE);
- for (i = 0; i < WALLCOUNT; i++)
- *p++ = m_wallPattern+i;
- p = (Int*)(((char*)getUserDataNoDbg()) + m_logicalSize);
- for (i = 0; i < WALLCOUNT; i++)
- *p++ = m_wallPattern-i;
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugVerifyBlock();
- #endif
- }
- #endif
- //-----------------------------------------------------------------------------
- // METHODS for MemoryPoolBlob
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- /**
- fill in safe default values.
- */
- MemoryPoolBlob::MemoryPoolBlob() :
- m_owningPool(NULL),
- m_nextBlob(NULL),
- m_prevBlob(NULL),
- m_firstFreeBlock(NULL),
- m_usedBlocksInBlob(0),
- m_totalBlocksInBlob(0),
- m_blockData(NULL)
- {
- }
- //-----------------------------------------------------------------------------
- /**
- throw away the blob. free the block data, if any.
- */
- MemoryPoolBlob::~MemoryPoolBlob()
- {
- ::sysFree((void *)m_blockData);
- }
- //-----------------------------------------------------------------------------
- /**
- initialize a Blob; this is called just after the blob is allocated.
- allocate space for the blocks in this blob and initialize all those blocks.
- */
- void MemoryPoolBlob::initBlob(MemoryPool *owningPool, Int allocationCount)
- {
- DEBUG_ASSERTCRASH(m_blockData == NULL, ("unlikely init call"));
- m_owningPool = owningPool;
- m_totalBlocksInBlob = allocationCount;
- m_usedBlocksInBlob = 0;
- Int rawBlockSize = MemoryPoolSingleBlock::calcRawBlockSize(m_owningPool->getAllocationSize());
- m_blockData = (char *)::sysAllocate(rawBlockSize * m_totalBlocksInBlob); // throws on failure
- // set up the list of free blocks in the blob (namely, all of 'em)
- MemoryPoolSingleBlock *block = (MemoryPoolSingleBlock *)m_blockData;
- MemoryPoolSingleBlock *next;
- for (Int i = m_totalBlocksInBlob-1; i >= 0; i--)
- {
- next = (MemoryPoolSingleBlock *)(((char *)block) + rawBlockSize);
- #ifdef MEMORYPOOL_DEBUG
- block->initBlock(m_owningPool->getAllocationSize(), this, owningPool->getOwningFactory(), FREE_SINGLEBLOCK_TAG_STRING);
- #else
- block->initBlock(m_owningPool->getAllocationSize(), this, owningPool->getOwningFactory());
- #endif
- block->setNextFreeBlock((i > 0) ? next : NULL);
- #ifdef MEMORYPOOL_DEBUG
- block->debugMarkBlockAsFree();
- #endif
- block = next;
- }
- m_firstFreeBlock = (MemoryPoolSingleBlock *)m_blockData;
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugMemoryVerifyBlob();
- #endif
- }
- //-----------------------------------------------------------------------------
- /**
- add this blob to a given pool's list-of-blobs
- */
- void MemoryPoolBlob::addBlobToList(MemoryPoolBlob **ppHead, MemoryPoolBlob **ppTail)
- {
- m_prevBlob = *ppTail;
- m_nextBlob = NULL;
- if (*ppTail != NULL)
- (*ppTail)->m_nextBlob = this;
- if (*ppHead == NULL)
- *ppHead = this;
- *ppTail = this;
- }
- //-----------------------------------------------------------------------------
- /**
- remove this blob from a given pool's list-of-blobs
- */
- void MemoryPoolBlob::removeBlobFromList(MemoryPoolBlob **ppHead, MemoryPoolBlob **ppTail)
- {
- if (*ppHead == this)
- *ppHead = this->m_nextBlob;
- else
- this->m_prevBlob->m_nextBlob = this->m_nextBlob;
-
- if (*ppTail == this)
- *ppTail = this->m_prevBlob;
- else
- this->m_nextBlob->m_prevBlob = this->m_prevBlob;
- }
- //-----------------------------------------------------------------------------
- /**
- grab a free block from this blob, mark it as taken, and return it.
- this method assumes that there is at least one free block in the blob!
- */
- MemoryPoolSingleBlock *MemoryPoolBlob::allocateSingleBlock(DECLARE_LITERALSTRING_ARG1)
- {
- DEBUG_ASSERTCRASH(m_firstFreeBlock, ("no free blocks available in MemoryPoolBlob"));
-
- MemoryPoolSingleBlock *block = m_firstFreeBlock;
- m_firstFreeBlock = block->getNextFreeBlock();
- ++m_usedBlocksInBlob;
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- block->debugVerifyBlock();
- #endif
- #ifdef MEMORYPOOL_DEBUG
- // this is debug-only because it only serves to update the debugLiteralTagString.
- block->initBlock(m_owningPool->getAllocationSize(), this, m_owningPool->getOwningFactory(), debugLiteralTagString);
- #endif
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugMemoryVerifyBlob();
- #endif
- // don't need to zero this out... the caller will do that, if necessary
- // memset(block->getUserData(), 0, m_owningPool->getAllocationSize());
- return block;
- }
- //-----------------------------------------------------------------------------
- /**
- make this block available for future allocations. it is assumed that the block
- belongs to this blob, and is not already free.
- */
- void MemoryPoolBlob::freeSingleBlock(MemoryPoolSingleBlock *block)
- {
- DEBUG_ASSERTCRASH(block->getOwningBlob() == this, ("block does not belong to this blob"));
- block->setNextFreeBlock(m_firstFreeBlock);
- m_firstFreeBlock = block;
- --m_usedBlocksInBlob;
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- block->debugVerifyBlock();
- #endif
- #ifdef MEMORYPOOL_DEBUG
- block->debugMarkBlockAsFree();
- #endif
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugMemoryVerifyBlob();
- #endif
-
- }
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- Perform internal consistency checking on this blob and all its blocks.
- */
- void MemoryPoolBlob::debugMemoryVerifyBlob()
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- DEBUG_ASSERTCRASH(m_owningPool != NULL, ("bad owner"));
- DEBUG_ASSERTCRASH(m_usedBlocksInBlob >= 0 && m_usedBlocksInBlob <= m_totalBlocksInBlob, ("unlikely m_usedBlocksInBlob"));
- DEBUG_ASSERTCRASH(m_totalBlocksInBlob > 0, ("unlikely m_totalBlocksInBlob"));
- Int rawBlockSize = MemoryPoolSingleBlock::calcRawBlockSize(m_owningPool->getAllocationSize());
- char *blockData = m_blockData;
- for (Int i = m_totalBlocksInBlob-1; i >= 0; i--, blockData += rawBlockSize)
- {
- MemoryPoolSingleBlock *block = (MemoryPoolSingleBlock *)blockData;
- block->debugVerifyBlock();
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- Int MemoryPoolBlob::debugBlobReportLeaks(const char* owner)
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) skip end-of-run reporting stuff
- Int any = 0;
- Int rawBlockSize = MemoryPoolSingleBlock::calcRawBlockSize(m_owningPool->getAllocationSize());
- char *blockData = m_blockData;
- for (Int i = m_totalBlocksInBlob-1; i >= 0; i--, blockData += rawBlockSize)
- {
- MemoryPoolSingleBlock *block = (MemoryPoolSingleBlock *)blockData;
- any += block->debugSingleBlockReportLeak(owner);
- }
- return any;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- return true iff this block belongs to this blob.
- */
- Bool MemoryPoolBlob::debugIsBlockInBlob(void *pBlockPtr)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- Int rawBlockSize = MemoryPoolSingleBlock::calcRawBlockSize(m_owningPool->getAllocationSize());
- char *blockData = m_blockData;
- for (Int i = m_totalBlocksInBlob-1; i >= 0; i--)
- {
- if ((char *)block == blockData)
- return true;
- blockData += rawBlockSize;
- }
- return false;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- set all the checkpointinfos to null for all the blocks in this blob.
- this does NOT free the checkpointinfos; that is presumed to happen elsewhere.
- */
- void MemoryPoolBlob::debugResetCheckpoints()
- {
- Int rawBlockSize = MemoryPoolSingleBlock::calcRawBlockSize(m_owningPool->getAllocationSize());
- char *blockData = m_blockData;
- for (Int i = m_totalBlocksInBlob-1; i >= 0; i--, blockData += rawBlockSize)
- {
- MemoryPoolSingleBlock *block = (MemoryPoolSingleBlock *)blockData;
- block->debugResetCheckpoint();
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- // METHODS for Checkpointable
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- //-----------------------------------------------------------------------------
- /**
- init fields of Checkpointable to safe values.
- */
- Checkpointable::Checkpointable() :
- m_firstCheckpointInfo(NULL),
- m_cpiEverFailed(false)
- {
- }
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- //-----------------------------------------------------------------------------
- /**
- destroy the object. discard any remaining checkpointinfo.
- */
- Checkpointable::~Checkpointable()
- {
- BlockCheckpointInfo::freeList(&m_firstCheckpointInfo);
- m_firstCheckpointInfo = NULL;
- m_cpiEverFailed = false;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- create a new checkpointinfo and fill it in appropriately. this does NOT
- throw an exception on failure; it quietly returns null if there is not
- enough memory, and sets a flag to indicate our checkpointinfo is not complete.
- */
- BlockCheckpointInfo *Checkpointable::debugAddCheckpointInfo(
- const char *debugLiteralTagString,
- Int allocCheckpoint,
- Int blockSize
- )
- {
- BlockCheckpointInfo *bi = BlockCheckpointInfo::addToList(&m_firstCheckpointInfo, debugLiteralTagString,
- allocCheckpoint, blockSize);
- if (bi)
- {
- #ifdef MEMORYPOOL_STACKTRACE
- void **stacktrace = bi->getStacktraceInfo();
- if (theStackTraceDepth > 0 && !TheGlobalData || TheGlobalData->m_checkForLeaks)
- {
- memset(stacktrace, 0, MEMORYPOOL_STACKTRACE_SIZE_BYTES);
- ::FillStackAddresses(stacktrace, min(MEMORYPOOL_STACKTRACE_SIZE, theStackTraceDepth), MEMORYPOOL_STACKTRACE_SKIP_SIZE);
- }
- else
- {
- stacktrace[0] = NULL;
- }
- #endif
- }
- else
- {
- m_cpiEverFailed = true;
- }
- return bi;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- print a report on the checkpointinfos belonging to this pool/dma.
- */
- void Checkpointable::debugCheckpointReport( Int flags, Int startCheckpoint, Int endCheckpoint, const char *poolName )
- {
- DEBUG_ASSERTCRASH(startCheckpoint >= 0 && startCheckpoint <= endCheckpoint, ("bad checkpoints"));
- DEBUG_ASSERTCRASH((flags & _REPORT_CP_ALLOCATED_DONTCARE) != 0, ("bad flags: must set at least one alloc flag"));
- DEBUG_ASSERTCRASH((flags & _REPORT_CP_FREED_DONTCARE) != 0, ("bad flags: must set at least one freed flag"));
- if (m_cpiEverFailed)
- {
- DEBUG_LOG((" *** WARNING *** info on freed blocks may be inaccurate or incomplete!\n"));
- }
- for (BlockCheckpointInfo *bi = m_firstCheckpointInfo; bi; bi = bi->getNext())
- {
- BlockCheckpointInfo::doBlockCheckpointReport( bi, poolName, flags, startCheckpoint, endCheckpoint );
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- throw away all the checkpointinfos. this frees the memory, but blocks might still
- refer to the discarded infos; you must zero those elsewhere.
- */
- void Checkpointable::debugResetCheckpoints()
- {
- BlockCheckpointInfo::freeList(&m_firstCheckpointInfo);
- }
- #endif
- //-----------------------------------------------------------------------------
- // METHODS for MemoryPool
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- /**
- init to safe values.
- */
- MemoryPool::MemoryPool() :
- m_factory(NULL),
- m_nextPoolInFactory(NULL),
- m_poolName(""),
- m_allocationSize(0),
- m_initialAllocationCount(0),
- m_overflowAllocationCount(0),
- m_usedBlocksInPool(0),
- m_totalBlocksInPool(0),
- m_peakUsedBlocksInPool(0),
- m_firstBlob(NULL),
- m_lastBlob(NULL),
- m_firstBlobWithFreeBlocks(NULL)
- {
- }
- //-----------------------------------------------------------------------------
- /**
- initialize the memory pool with the given parameters. allocate the initial
- set of blocks.
- */
- void MemoryPool::init(MemoryPoolFactory *factory, const char *poolName, Int allocationSize, Int initialAllocationCount, Int overflowAllocationCount)
- {
- m_factory = factory;
- m_poolName = poolName;
- m_allocationSize = ::roundUpMemBound(allocationSize); // round up to four-byte boundary
- m_initialAllocationCount = initialAllocationCount;
- m_overflowAllocationCount = overflowAllocationCount;
- m_usedBlocksInPool = 0;
- m_totalBlocksInPool = 0;
- m_peakUsedBlocksInPool = 0;
- m_firstBlob = NULL;
- m_lastBlob = NULL;
- m_firstBlobWithFreeBlocks = NULL;
- // go ahead and init the initial block here (will throw on failure)
- createBlob(m_initialAllocationCount);
- }
- //-----------------------------------------------------------------------------
- /**
- throw away the pool, and all blocks/blobs associated with it.
- */
- MemoryPool::~MemoryPool()
- {
- // toss everything. we could do this slightly more efficiently,
- // but not really worth the extra code to do so.
- while (m_firstBlob)
- {
- freeBlob(m_firstBlob);
- }
- }
- //-----------------------------------------------------------------------------
- /**
- create a new blob for this pool. if you set up good values for initialAllocationCount,
- this should rarely (if ever) be called (though during development it will be called
- frequently).
- */
- MemoryPoolBlob* MemoryPool::createBlob(Int allocationCount)
- {
- DEBUG_ASSERTCRASH(allocationCount > 0 && allocationCount%MEM_BOUND_ALIGNMENT==0, ("bad allocationCount (must be >0 and evenly divisible by %d)",MEM_BOUND_ALIGNMENT));
- MemoryPoolBlob* blob = new (::sysAllocate(sizeof MemoryPoolBlob)) MemoryPoolBlob; // will throw on failure
- blob->initBlob(this, allocationCount); // will throw on failure
- blob->addBlobToList(&m_firstBlob, &m_lastBlob);
- DEBUG_ASSERTCRASH(m_firstBlobWithFreeBlocks == NULL, ("DO NOT IGNORE. Please call John McD - x36872 (m_firstBlobWithFreeBlocks != NULL)"));
- m_firstBlobWithFreeBlocks = blob;
- // bookkeeping
- m_totalBlocksInPool += allocationCount;
- #ifdef MEMORYPOOL_DEBUG
- m_factory->adjustTotals("", 0, allocationCount*getAllocationSize());
- #endif
- return blob;
- }
-
- //-----------------------------------------------------------------------------
- /**
- throw away a given blob, and all its blocks. it's assumed that the blob belongs
- to this pool.
- */
- Int MemoryPool::freeBlob(MemoryPoolBlob* blob)
- {
- DEBUG_ASSERTCRASH(blob, ("null blob"));
- DEBUG_ASSERTCRASH(blob->getOwningPool() == this, ("blob does not belong to this pool"));
- // save these for later...
- Int totalBlocksInBlob = blob->getTotalBlockCount();
- Int usedBlocksInBlob = blob->getUsedBlockCount();
- DEBUG_ASSERTCRASH(usedBlocksInBlob == 0, ("freeing a nonempty blob (%d)\n",usedBlocksInBlob));
- // this is really just an estimate... will be too small in debug mode.
- Int amtFreed = totalBlocksInBlob * getAllocationSize() + sizeof(MemoryPoolBlob);
- // de-link it from our list
- blob->removeBlobFromList(&m_firstBlob, &m_lastBlob);
-
- // ensure that the 'first free' blob is still a valid blob.
- // (doesn't need to actually have free blocks, just be a valid blob)
- if (m_firstBlobWithFreeBlocks == blob)
- m_firstBlobWithFreeBlocks = m_firstBlob;
- // this is evil... since there is no 'placement delete' we must do this the hard way
- // and call the dtor directly. ordinarily this is heinous, but in this case we'll
- // make an exception.
- blob->~MemoryPoolBlob();
- ::sysFree((void *)blob);
- // finally... bookkeeping
- m_usedBlocksInPool -= usedBlocksInBlob;
- m_totalBlocksInPool -= totalBlocksInBlob;
- #ifdef MEMORYPOOL_DEBUG
- m_factory->adjustTotals("", -usedBlocksInBlob*getAllocationSize(), -totalBlocksInBlob*getAllocationSize());
- #endif
- return amtFreed;
- }
- //-----------------------------------------------------------------------------
- /**
- allocate a block from this pool and return it, but don't bother zeroing
- out the block. if unable to allocate, throw ERROR_OUT_OF_MEMORY. this
- function will never return null.
- */
- void* MemoryPool::allocateBlockDoNotZeroImplementation(DECLARE_LITERALSTRING_ARG1)
- {
- ScopedCriticalSection scopedCriticalSection(TheMemoryPoolCriticalSection);
- if (m_firstBlobWithFreeBlocks != NULL && !m_firstBlobWithFreeBlocks->hasAnyFreeBlocks())
- {
- // hmm... the current 'free' blob has nothing available. look and see if there
- // are any other existing blobs with freespace.
- for (MemoryPoolBlob *blob = m_firstBlob; blob != NULL; blob = blob->getNextInList())
- {
- if (blob->hasAnyFreeBlocks())
- break;
- }
- // note that if we walk thru the list without finding anything, this will
- // reset m_firstBlobWithFreeBlocks to null and fall thru.
- m_firstBlobWithFreeBlocks = blob;
- }
-
- // OK, if we are here then we have no blobs with freespace... darn.
- // allocate an overflow block.
- if (m_firstBlobWithFreeBlocks == NULL)
- {
- if (m_overflowAllocationCount == 0)
- {
- throw ERROR_OUT_OF_MEMORY; // this pool is not allowed to grow
- }
- else
- {
- createBlob(m_overflowAllocationCount); // throws on failure
- }
- }
-
- MemoryPoolBlob *blob = m_firstBlobWithFreeBlocks;
- DEBUG_ASSERTCRASH(blob, ("no blob with free blocks available in MemoryPool::allocate"));
-
- MemoryPoolSingleBlock *block = blob->allocateSingleBlock(PASS_LITERALSTRING_ARG1);
- DEBUG_ASSERTCRASH(block, ("should not fail here"));
- #ifdef MEMORYPOOL_CHECKPOINTING
- BlockCheckpointInfo *bi = debugAddCheckpointInfo(block->debugGetLiteralTagString(), m_factory->getCurCheckpoint(), getAllocationSize());
- if (bi)
- block->debugSetCheckpointInfo(bi);
- #endif
- // bookkeeping
- ++m_usedBlocksInPool;
- if (m_peakUsedBlocksInPool < m_usedBlocksInPool)
- m_peakUsedBlocksInPool = m_usedBlocksInPool;
- #ifdef MEMORYPOOL_DEBUG
- m_factory->adjustTotals(debugLiteralTagString, 1*getAllocationSize(), 0);
- #ifdef USE_FILLER_VALUE
- {
- USE_PERF_TIMER(MemoryPoolInitFilling)
- ::memset32(block->getUserData(), s_initFillerValue, getAllocationSize());
- }
- #endif
- #endif
- return block->getUserData();
- }
- //-----------------------------------------------------------------------------
- /**
- allocate a block from this pool and return it, and zero out the contents
- of the block. if unable to allocate, throw ERROR_OUT_OF_MEMORY. this
- function will never return null.
- */
- void* MemoryPool::allocateBlockImplementation(DECLARE_LITERALSTRING_ARG1)
- {
- void* p = allocateBlockDoNotZeroImplementation(PASS_LITERALSTRING_ARG1); // throws on failure
- memset(p, 0, getAllocationSize());
- return p;
- }
- //-----------------------------------------------------------------------------
- /**
- free a block allocated by this pool. it's ok to pass null.
- */
- void MemoryPool::freeBlock(void* pBlockPtr)
- {
- if (!pBlockPtr)
- return; // my, that was easy
- ScopedCriticalSection scopedCriticalSection(TheMemoryPoolCriticalSection);
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- MemoryPoolBlob *blob = block->getOwningBlob();
- #ifdef MEMORYPOOL_DEBUG
- const char* tagString = block->debugGetLiteralTagString();
- #endif
-
- DEBUG_ASSERTCRASH(blob && blob->getOwningPool() == this, ("block does not belong to this pool"));
- #ifdef MEMORYPOOL_CHECKPOINTING
- BlockCheckpointInfo *bi = block->debugGetCheckpointInfo();
- DEBUG_ASSERTCRASH(bi, ("hmm, no checkpoint info"));
- if (bi)
- bi->debugSetFreepoint(m_factory->getCurCheckpoint());
- #endif
- blob->freeSingleBlock(block);
-
- // if we want to free the blobs as they become empty, do that here.
- // normally we don't bother, but just in case this is ever desired, here's how you'd do it...
- //
- // if (blob->m_usedBlocksInBlob == 0)
- // {
- // freeBlob(blob);
- // return;
- //}
-
- if (!m_firstBlobWithFreeBlocks)
- m_firstBlobWithFreeBlocks = blob;
- // bookkeeping
- --m_usedBlocksInPool;
- #ifdef MEMORYPOOL_DEBUG
- m_factory->adjustTotals(tagString, -1*getAllocationSize(), 0);
- #endif
- }
- //-----------------------------------------------------------------------------
- Int MemoryPool::countBlobsInPool()
- {
- Int blobs = 0;
- for (MemoryPoolBlob* blob = m_firstBlob; blob;)
- {
- ++blobs;
- blob = blob->getNextInList();
- }
- return blobs;
- }
- //-----------------------------------------------------------------------------
- /**
- if the pool has any blobs that are completely unused, they are released back to the
- operating system. this will rarely, if ever, be called, but may be useful
- in odd situations.
- */
- Int MemoryPool::releaseEmpties()
- {
- ScopedCriticalSection scopedCriticalSection(TheMemoryPoolCriticalSection);
- Int released = 0;
- for (MemoryPoolBlob* blob = m_firstBlob; blob;)
- {
- MemoryPoolBlob* pNext = blob->getNextInList();
- if (blob->getUsedBlockCount() == 0)
- released += freeBlob(blob);
- blob = pNext;
- }
- return released;
- }
- //-----------------------------------------------------------------------------
- /**
- throw away everything in the pool, but keep the pool itself valid.
- */
- void MemoryPool::reset()
- {
- ScopedCriticalSection scopedCriticalSection(TheMemoryPoolCriticalSection);
- // toss everything. we could do this slightly more efficiently,
- // but not really worth the extra code to do so.
- while (m_firstBlob)
- {
- freeBlob(m_firstBlob);
- }
- m_firstBlob = NULL;
- m_lastBlob = NULL;
- m_firstBlobWithFreeBlocks = NULL;
- init(m_factory, m_poolName, m_allocationSize, m_initialAllocationCount, m_overflowAllocationCount); // will throw on failure
- }
- //-----------------------------------------------------------------------------
- /**
- add this pool to the factory's list-of-pools.
- */
- void MemoryPool::addToList(MemoryPool **pHead)
- {
- this->m_nextPoolInFactory = *pHead;
- *pHead = this;
- }
- //-----------------------------------------------------------------------------
- /**
- remove this pool from the factory's list-of-pools.
- */
- void MemoryPool::removeFromList(MemoryPool **pHead)
- {
- // this isn't very efficient, but then, we rarely remove pools...
- // usually only at shutdown. so don't bother optimizing.
- MemoryPool *prev = NULL;
- for (MemoryPool *cur = *pHead; cur; cur = cur->m_nextPoolInFactory)
- {
- if (cur == this)
- {
- if (prev)
- {
- prev->m_nextPoolInFactory = this->m_nextPoolInFactory;
- }
- else
- {
- *pHead = this->m_nextPoolInFactory;
- }
- break;
- }
- prev = cur;
- }
- }
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- print a report about per-pool allocation statistics to the debug log.
- if the pool is null, print column headers.
- */
- /*static*/ void MemoryPool::debugPoolInfoReport( MemoryPool *pool, FILE *fp )
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) skip end-of-run reporting stuff
- const char *PREPEND = "POOLINFO"; // allows grepping more easily
- if (!pool)
- {
- DEBUG_LOG(("%s,%32s,%6s,%6s,%6s,%6s,%6s,%6s\n",PREPEND,"POOLNAME","BLKSZ","INIT","OVRFL","USED","TOTAL","PEAK"));
- if( fp )
- fprintf( fp, "%s,%32s,%6s,%6s,%6s,%6s,%6s,%6s\n",PREPEND,"POOLNAME","BLKSZ","INIT","OVRFL","USED","TOTAL","PEAK" );
- }
- else
- {
- DEBUG_LOG(("%s,%32s,%6d,%6d,%6d,%6d,%6d,%6d\n",PREPEND,
- pool->m_poolName,pool->m_allocationSize,pool->m_initialAllocationCount,pool->m_overflowAllocationCount,
- pool->m_usedBlocksInPool,pool->m_totalBlocksInPool,pool->m_peakUsedBlocksInPool));
- if( fp )
- {
- fprintf( fp, "%s,%32s,%6d,%6d,%6d,%6d,%6d,%6d\n",PREPEND,
- pool->m_poolName,pool->m_allocationSize,pool->m_initialAllocationCount,pool->m_overflowAllocationCount,
- pool->m_usedBlocksInPool,pool->m_totalBlocksInPool,pool->m_peakUsedBlocksInPool );
- }
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- Int MemoryPool::debugPoolReportLeaks( const char* owner )
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) skip end-of-run reporting stuff
- Int any = 0;
- for (MemoryPoolBlob* blob = m_firstBlob; blob; blob = blob->getNextInList())
- {
- any += blob->debugBlobReportLeaks(owner);
- }
- return any;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- perform internal consistency checking on the memory pool.
- */
- void MemoryPool::debugMemoryVerifyPool()
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- Int used = 0;
- Int total = 0;
- for (MemoryPoolBlob* blob = m_firstBlob; blob; blob = blob->getNextInList())
- {
- blob->debugMemoryVerifyBlob();
- used += blob->getUsedBlockCount();
- total += blob->getTotalBlockCount();
- }
- DEBUG_ASSERTCRASH(m_usedBlocksInPool == used, ("used mismatch %d %d",m_usedBlocksInPool,used));
- DEBUG_ASSERTCRASH(m_totalBlocksInPool == total, ("total mismatch %d %d",m_totalBlocksInPool,total));
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- return true iff the block is a valid block in this pool.
- */
- Bool MemoryPool::debugIsBlockInPool(void *pBlockPtr)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- if (!pBlockPtr)
- return false;
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugMemoryVerifyPool();
- #endif
- Bool check1 = false, check2 = false;
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- MemoryPoolBlob *ownerBlob = block->getOwningBlob();
- for (MemoryPoolBlob* blob = m_firstBlob; blob; blob = blob->getNextInList())
- {
- if (blob->debugIsBlockInBlob(pBlockPtr))
- check1 = true;
-
- if (blob == ownerBlob)
- check2 = true;
- }
- DEBUG_ASSERTCRASH(check1 == check2, ("mismatch checks in debugIsBlockInPool"));
- return check1 && check2;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- return the tagstring for the block. this will never return null; if
- the block is null or invalid, "FREE_SINGLEBLOCK_TAG_STRING" will be returned.
- it is assumed that the block was allocated by this pool.
- */
- const char *MemoryPool::debugGetBlockTagString(void *pBlockPtr)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- if (!pBlockPtr)
- return FREE_SINGLEBLOCK_TAG_STRING;
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugMemoryVerifyPool();
- #endif
- if (!debugIsBlockInPool(pBlockPtr))
- {
- DEBUG_CRASH(("block is not in this pool"));
- return FREE_SINGLEBLOCK_TAG_STRING;
- }
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- return block->debugGetLiteralTagString();
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- free all checkpointinfo for this pool, and reset all ptrs to checkpointinfo.
- */
- void MemoryPool::debugResetCheckpoints()
- {
- Checkpointable::debugResetCheckpoints();
- for (MemoryPoolBlob* blob = m_firstBlob; blob; blob = blob->getNextInList())
- {
- blob->debugResetCheckpoints();
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- // METHODS for DynamicMemoryAllocator
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- /**
- init the DMA to safe values.
- */
- DynamicMemoryAllocator::DynamicMemoryAllocator() :
- m_factory(NULL),
- m_nextDmaInFactory(NULL),
- m_numPools(0),
- m_usedBlocksInDma(0),
- m_rawBlocks(NULL)
- {
- for (Int i = 0; i < MAX_DYNAMICMEMORYALLOCATOR_SUBPOOLS; i++)
- m_pools[i] = 0;
- }
- //-----------------------------------------------------------------------------
- /**
- Initialize the dma and its subpools.
- */
- void DynamicMemoryAllocator::init(MemoryPoolFactory *factory, Int numSubPools, const PoolInitRec pParms[])
- {
- const PoolInitRec defaultDMA[7] =
- {
- { "dmaPool_16", 16, 64, 64 },
- { "dmaPool_32", 32, 64, 64 },
- { "dmaPool_64", 64, 64, 64 },
- { "dmaPool_128", 128, 64, 64 },
- { "dmaPool_256", 256, 64, 64 },
- { "dmaPool_512", 512, 64, 64 },
- { "dmaPool_1024", 1024, 64, 64 }
- };
- if (numSubPools == 0 || pParms == NULL)
- {
- // use the defaults...
- numSubPools = 7;
- pParms = defaultDMA;
- }
- m_factory = factory;
- m_numPools = numSubPools;
- if (m_numPools > MAX_DYNAMICMEMORYALLOCATOR_SUBPOOLS)
- m_numPools = MAX_DYNAMICMEMORYALLOCATOR_SUBPOOLS;
- m_usedBlocksInDma = 0;
- for (Int i = 0; i < m_numPools; i++)
- {
- DEBUG_ASSERTCRASH(i == 0 || pParms[i].allocationSize > pParms[i-1].allocationSize, ("alloc size must increase monotonically for DMA"));
- m_pools[i] = m_factory->createMemoryPool(&pParms[i]);
- }
- }
- //-----------------------------------------------------------------------------
- /**
- destroy the dma and its subpools.
- */
- DynamicMemoryAllocator::~DynamicMemoryAllocator()
- {
- DEBUG_ASSERTCRASH(m_usedBlocksInDma == 0, ("destroying a nonempty dma"));
- /// @todo this may cause double-destruction of the subpools -- test & fix
- for (Int i = 0; i < m_numPools; i++)
- {
- m_factory->destroyMemoryPool(m_pools[i]);
- m_pools[i] = NULL;
- }
- while (m_rawBlocks)
- {
- freeBytes(m_rawBlocks->getUserData());
- }
- }
- //-----------------------------------------------------------------------------
- /**
- find the best-fitting subpool in this dma for a given allocation size.
- if no subpool can satisfy the size, return null.
- */
- MemoryPool *DynamicMemoryAllocator::findPoolForSize(Int allocSize)
- {
- for (Int i = 0; i < m_numPools; i++)
- {
- DEBUG_ASSERTCRASH(m_pools[i], ("null pool"));
- if (allocSize <= m_pools[i]->getAllocationSize())
- return m_pools[i];
- }
- return NULL;
- }
- //-----------------------------------------------------------------------------
- /**
- add this DMA to the factory's list of dmas.
- */
- void DynamicMemoryAllocator::addToList(DynamicMemoryAllocator **pHead)
- {
- this->m_nextDmaInFactory = *pHead;
- *pHead = this;
- }
- //-----------------------------------------------------------------------------
- /**
- remove this DMA from the factory's list of dmas.
- */
- void DynamicMemoryAllocator::removeFromList(DynamicMemoryAllocator **pHead)
- {
- // this isn't very efficient, but then, we rarely remove these...
- // usually only at shutdown. so don't bother optimizing.
- DynamicMemoryAllocator *prev = NULL;
- for (DynamicMemoryAllocator *cur = *pHead; cur; cur = cur->m_nextDmaInFactory)
- {
- if (cur == this)
- {
- if (prev)
- {
- prev->m_nextDmaInFactory = this->m_nextDmaInFactory;
- }
- else
- {
- *pHead = this->m_nextDmaInFactory;
- }
- break;
- }
- prev = cur;
- }
- }
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- void DynamicMemoryAllocator::debugIgnoreLeaksForThisBlock(void* pBlockPtr)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- if (!pBlockPtr)
- return;
- #ifdef MEMORYPOOL_CHECK_BLOCK_OWNERSHIP
- DEBUG_ASSERTCRASH(debugIsBlockInDma(pBlockPtr), ("block is not in this dma"));
- #endif
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- if (block->getOwningBlob())
- {
- #ifdef MEMORYPOOL_DEBUG
- DEBUG_ASSERTCRASH(findPoolForSize(block->debugGetLogicalSize()) == block->getOwningBlob()->getOwningPool(), ("pool mismatch"));
- #endif
- block->debugIgnoreLeaksForThisBlock();
- }
- else
- {
- DEBUG_CRASH(("cannot currently ignore leaks for raw blocks (allocation too large)\n"));
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- /**
- allocate a chunk-o-bytes from this DMA and return it, but don't bother zeroing
- out the block. if unable to allocate, throw ERROR_OUT_OF_MEMORY. this
- function will never return null.
- */
- void *DynamicMemoryAllocator::allocateBytesDoNotZeroImplementation(Int numBytes DECLARE_LITERALSTRING_ARG2)
- {
- ScopedCriticalSection scopedCriticalSection(TheDmaCriticalSection);
- void *result = NULL;
- #ifdef MEMORYPOOL_DEBUG
- DEBUG_ASSERTCRASH(debugLiteralTagString != NULL, ("bad tagstring"));
- Int waste = 0;
- #endif
- MemoryPool *pool = findPoolForSize(numBytes);
- if (pool != NULL)
- {
- result = pool->allocateBlockDoNotZeroImplementation(PASS_LITERALSTRING_ARG1);
- #ifdef MEMORYPOOL_DEBUG
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- waste = pool->getAllocationSize() - numBytes;
- MemoryPoolSingleBlock *wblock = MemoryPoolSingleBlock::recoverBlockFromUserData(result);
- wblock->debugSetWastedSize(waste);
- #ifdef INTENSE_DMA_BOOKKEEPING
- if (doingIntenseDMA == 0)
- #endif
- {
- theWastedDMA += (waste);
- if (thePeakWastedDMA < theWastedDMA)
- thePeakWastedDMA = theWastedDMA;
- }
- }
- #endif MEMORYPOOL_DEBUG
- }
- else
- {
- // too big for our pools -- just go right to the metal.
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::rawAllocateSingleBlock(&m_rawBlocks, numBytes, m_factory PASS_LITERALSTRING_ARG2);
- #ifdef MEMORYPOOL_CHECKPOINTING
- BlockCheckpointInfo *bi = debugAddCheckpointInfo(block->debugGetLiteralTagString(), m_factory->getCurCheckpoint(), numBytes);
- if (bi)
- block->debugSetCheckpointInfo(bi);
- #endif
- result = block->getUserData();
- #ifdef MEMORYPOOL_DEBUG
- m_factory->adjustTotals(debugLiteralTagString, numBytes, numBytes);
- theTotalLargeBlocks += numBytes;
- if (thePeakLargeBlocks < theTotalLargeBlocks)
- thePeakLargeBlocks = theTotalLargeBlocks;
- #endif
- }
- #ifdef MEMORYPOOL_DEBUG
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- theTotalDMA += numBytes;
- if (thePeakDMA < theTotalDMA)
- thePeakDMA = theTotalDMA;
- #ifdef INTENSE_DMA_BOOKKEEPING
- if (isMemoryManagerOfficiallyInited() && doingIntenseDMA == 0)
- {
- ++doingIntenseDMA;
- UsedNPeak& up = TheUsedNPeakMap[debugLiteralTagString];
- up.used += numBytes;
- if (up.peak < up.used)
- up.peak = up.used;
- up.waste += waste;
- if (up.peakwaste < up.waste)
- up.peakwaste = up.waste;
- --doingIntenseDMA;
- }
- #endif
- }
- #endif MEMORYPOOL_DEBUG
- ++m_usedBlocksInDma;
- DEBUG_ASSERTCRASH(m_usedBlocksInDma >= 0, ("negative count for m_usedBlocksInDma"));
- #ifdef MEMORYPOOL_DEBUG
- #ifdef USE_FILLER_VALUE
- {
- USE_PERF_TIMER(MemoryPoolInitFilling)
- ::memset32(result, s_initFillerValue, numBytes);
- }
- #endif
- #endif
- return result;
- }
- //-----------------------------------------------------------------------------
- /**
- allocate a chunk-o-bytes from this DMA and return it, and zero out the contents first.
- if unable to allocate, throw ERROR_OUT_OF_MEMORY.
- this function will never return null.
- */
- void *DynamicMemoryAllocator::allocateBytesImplementation(Int numBytes DECLARE_LITERALSTRING_ARG2)
- {
- void* p = allocateBytesDoNotZeroImplementation(numBytes PASS_LITERALSTRING_ARG2); // throws on failure
- memset(p, 0, numBytes);
- return p;
- }
- //-----------------------------------------------------------------------------
- /**
- free a chunk-o-bytes allocated by this dma. it's ok to pass null.
- */
- void DynamicMemoryAllocator::freeBytes(void* pBlockPtr)
- {
- if (!pBlockPtr)
- return;
- ScopedCriticalSection scopedCriticalSection(TheDmaCriticalSection);
- #ifdef MEMORYPOOL_CHECK_BLOCK_OWNERSHIP
- DEBUG_ASSERTCRASH(debugIsBlockInDma(pBlockPtr), ("block is not in this dma"));
- #endif
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- #ifdef MEMORYPOOL_DEBUG
- Int waste = 0, used = 0;
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- waste = 0;
- used = block->debugGetLogicalSize();
- theTotalDMA -= used;
- if (thePeakDMA < theTotalDMA)
- thePeakDMA = theTotalDMA;
- #ifdef INTENSE_DMA_BOOKKEEPING
- const char* tagString = block->debugGetLiteralTagString();
- #endif
- }
- #endif MEMORYPOOL_DEBUG
- if (block->getOwningBlob())
- {
- #ifdef MEMORYPOOL_DEBUG
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- DEBUG_ASSERTCRASH(findPoolForSize(used) == block->getOwningBlob()->getOwningPool(), ("pool mismatch"));
- #ifdef INTENSE_DMA_BOOKKEEPING
- if (doingIntenseDMA == 0)
- #endif
- {
- waste = block->debugGetWastedSize();
- theWastedDMA -= waste;
- if (thePeakWastedDMA < theWastedDMA)
- thePeakWastedDMA = theWastedDMA;
- }
- }
- #endif MEMORYPOOL_DEBUG
- block->getOwningBlob()->getOwningPool()->freeBlock(pBlockPtr);
- }
- else
- {
- // was allocated via sysAllocate.
- #ifdef MEMORYPOOL_CHECKPOINTING
- BlockCheckpointInfo *bi = block->debugGetCheckpointInfo();
- DEBUG_ASSERTCRASH(bi, ("hmm, no checkpoint info"));
- if (bi)
- bi->debugSetFreepoint(m_factory->getCurCheckpoint());
- #endif
- #ifdef MEMORYPOOL_DEBUG
- m_factory->adjustTotals(block->debugGetLiteralTagString(), -used, -used);
- theTotalLargeBlocks -= used;
- if (thePeakLargeBlocks < theTotalLargeBlocks)
- thePeakLargeBlocks = theTotalLargeBlocks;
- block->debugMarkBlockAsFree();
- #endif
- block->removeBlockFromList(&m_rawBlocks);
- ::sysFree((void *)block);
- }
- --m_usedBlocksInDma;
- DEBUG_ASSERTCRASH(m_usedBlocksInDma >= 0, ("negative count for m_usedBlocksInDma"));
- #ifdef INTENSE_DMA_BOOKKEEPING
- if (isMemoryManagerOfficiallyInited() && doingIntenseDMA == 0)
- {
- ++doingIntenseDMA;
- UsedNPeak& up = TheUsedNPeakMap[tagString];
- up.used -= used;
- if (up.peak < up.used)
- up.peak = up.used;
- up.waste -= waste;
- if (up.peakwaste < up.waste)
- up.peakwaste = up.waste;
- --doingIntenseDMA;
- }
- #endif
- }
- //-----------------------------------------------------------------------------
- Int DynamicMemoryAllocator::getActualAllocationSize(Int numBytes)
- {
- MemoryPool *pool = findPoolForSize(numBytes);
- return pool ? pool->getAllocationSize() : numBytes;
- }
- //-----------------------------------------------------------------------------
- /**
- throw away everything in the DMA, but keep the DMA itself valid.
- */
- void DynamicMemoryAllocator::reset()
- {
- for (Int i = 0; i < m_numPools; i++)
- {
- if (m_pools[i])
- {
- m_pools[i]->reset();
- }
- }
- while (m_rawBlocks)
- freeBytes(m_rawBlocks->getUserData());
- m_usedBlocksInDma = 0;
- }
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- return true iff the given pool is a subpool of this dma.
- */
- Bool DynamicMemoryAllocator::debugIsPoolInDma(MemoryPool *pool)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- if (!pool)
- return false;
- for (Int i = 0; i < m_numPools; i++)
- {
- if (m_pools[i] == pool)
- return true;
- }
- return false;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- return true iff the block was allocated by this dma
- (either from a subpool or as a raw block).
- */
- Bool DynamicMemoryAllocator::debugIsBlockInDma(void *pBlockPtr)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- if (!pBlockPtr)
- return false;
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- if (block->getOwningBlob())
- {
- MemoryPool *pool = block->getOwningBlob()->getOwningPool();
- return pool && pool->debugIsBlockInPool(pBlockPtr) && debugIsPoolInDma(pool);
- }
- else
- {
- for (MemoryPoolSingleBlock *b = m_rawBlocks; b; b = b->getNextRawBlock())
- {
- if (b == block)
- return true;
- }
- return false;
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- return the tagstring for the block. this will never return null; if
- the block is null or invalid, "FREE_SINGLEBLOCK_TAG_STRING" will be returned.
- it is assumed that the block was allocated by this dma.
- */
- const char *DynamicMemoryAllocator::debugGetBlockTagString(void *pBlockPtr)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- if (!pBlockPtr)
- return FREE_SINGLEBLOCK_TAG_STRING;
- if (!debugIsBlockInDma(pBlockPtr))
- {
- DEBUG_CRASH(("block is not in this dma"));
- return FREE_SINGLEBLOCK_TAG_STRING;
- }
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- return block->debugGetLiteralTagString();
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- perform internal consistency checking on this DMA.
- */
- void DynamicMemoryAllocator::debugMemoryVerifyDma()
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- for (MemoryPoolSingleBlock *b = m_rawBlocks; b; b = b->getNextRawBlock())
- {
- b->debugVerifyBlock();
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- free all checkpointinfo for this dma, and reset all ptrs to checkpointinfo.
- */
- void DynamicMemoryAllocator::debugResetCheckpoints()
- {
- Checkpointable::debugResetCheckpoints();
- for (MemoryPoolSingleBlock *b = m_rawBlocks; b; b = b->getNextRawBlock())
- {
- b->debugResetCheckpoint();
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- calculate the total number of raw blocks allocated by this dma,
- and the total number of bytes (logical) used by those raw blocks.
- */
- Int DynamicMemoryAllocator::debugCalcRawBlockBytes(Int *numBlocks)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- if (numBlocks)
- *numBlocks = 0;
- Int bytes = 0;
- for (MemoryPoolSingleBlock *b = m_rawBlocks; b; b = b->getNextRawBlock())
- {
- if (numBlocks)
- *numBlocks += 1;
- bytes += b->debugGetLogicalSize();
- }
- return bytes;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- Int DynamicMemoryAllocator::debugDmaReportLeaks()
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) skip end-of-run reporting stuff
- Int any = false;
- for (MemoryPoolSingleBlock *b = m_rawBlocks; b; b = b->getNextRawBlock())
- {
- any += b->debugSingleBlockReportLeak("(DMA)");
- }
- return any;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- print a report about raw block allocations to the debug log.
- */
- void DynamicMemoryAllocator::debugDmaInfoReport( FILE *fp )
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) skip end-of-run reporting stuff
- const char *PREPEND = "POOLINFO"; // allows grepping more easily
- Int numBlocks;
- Int bytes = debugCalcRawBlockBytes(&numBlocks);
- DEBUG_LOG(("%s,Total Raw Blocks = %d\n",PREPEND,numBlocks));
- DEBUG_LOG(("%s,Total Raw Block Bytes = %d\n",PREPEND,bytes));
- DEBUG_LOG(("%s,Average Raw Block Size = %d\n",PREPEND,numBlocks?bytes/numBlocks:0));
- DEBUG_LOG(("%s,Raw Blocks:\n",PREPEND));
- if( fp )
- {
- fprintf( fp, "%s,Total Raw Blocks = %d\n",PREPEND,numBlocks );
- fprintf( fp, "%s,Total Raw Block Bytes = %d\n",PREPEND,bytes );
- fprintf( fp, "%s,Average Raw Block Size = %d\n",PREPEND,numBlocks?bytes/numBlocks:0 );
- fprintf( fp, "%s,Raw Blocks:\n",PREPEND );
- }
- for (MemoryPoolSingleBlock *b = m_rawBlocks; b; b = b->getNextRawBlock())
- {
- DEBUG_LOG(("%s, Blocksize=%d\n",PREPEND,b->debugGetLogicalSize()));
- //if( fp )
- //{
- // fprintf( fp, "%s, Blocksize=%d\n",PREPEND,b->debugGetLogicalSize() );
- //}
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- // METHODS for MemoryPoolFactory
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- /**
- init the factory to safe values.
- */
- MemoryPoolFactory::MemoryPoolFactory() :
- m_firstPoolInFactory(NULL),
- m_firstDmaInFactory(NULL)
- #ifdef MEMORYPOOL_CHECKPOINTING
- , m_curCheckpoint(0)
- #endif
- #ifdef MEMORYPOOL_DEBUG
- , m_usedBytes(0)
- , m_physBytes(0)
- , m_peakUsedBytes(0)
- , m_peakPhysBytes(0)
- #endif
- {
- #ifdef MEMORYPOOL_DEBUG
- for (int i = 0; i < MAX_SPECIAL_USED; ++i)
- {
- m_usedBytesSpecial[i] = 0;
- m_usedBytesSpecialPeak[i] = 0;
- m_physBytesSpecial[i] = 0;
- m_physBytesSpecialPeak[i] = 0;
- }
- #ifdef USE_FILLER_VALUE
- calcFillerValue(GameClientRandomValue(0, MAX_INIT_FILLER_COUNT-1));
- #endif
- #endif
- }
- //-----------------------------------------------------------------------------
- /**
- initialize the factory.
- */
- void MemoryPoolFactory::init()
- {
- // my, that was easy
- }
- //-----------------------------------------------------------------------------
- /**
- destroy the factory, and all its pools and dmas.
- */
- MemoryPoolFactory::~MemoryPoolFactory()
- {
- while (m_firstPoolInFactory)
- {
- destroyMemoryPool(m_firstPoolInFactory);
- }
- while (m_firstDmaInFactory)
- {
- destroyDynamicMemoryAllocator(m_firstDmaInFactory);
- }
- }
- //-----------------------------------------------------------------------------
- /**
- create a new memory pool.
- */
- MemoryPool *MemoryPoolFactory::createMemoryPool(const PoolInitRec *parms)
- {
- return createMemoryPool(parms->poolName, parms->allocationSize, parms->initialAllocationCount, parms->overflowAllocationCount);
- }
- //-----------------------------------------------------------------------------
- /**
- create a new memory pool. (alternate argument list)
- */
- MemoryPool *MemoryPoolFactory::createMemoryPool(const char *poolName, Int allocationSize, Int initialAllocationCount, Int overflowAllocationCount)
- {
- MemoryPool *pool = findMemoryPool(poolName);
- if (pool)
- {
- DEBUG_ASSERTCRASH(allocationSize == pool->getAllocationSize(), ("pool size mismatch"));
- return pool;
- }
- userMemoryAdjustPoolSize(poolName, initialAllocationCount, overflowAllocationCount);
- if (initialAllocationCount <= 0 || overflowAllocationCount < 0)
- {
- DEBUG_CRASH(("illegal pool size: %d %d\n",initialAllocationCount,overflowAllocationCount));
- throw ERROR_OUT_OF_MEMORY;
- }
- pool = new (::sysAllocate(sizeof MemoryPool)) MemoryPool; // will throw on failure
- pool->init(this, poolName, allocationSize, initialAllocationCount, overflowAllocationCount); // will throw on failure
- pool->addToList(&m_firstPoolInFactory);
- return pool;
- }
- //-----------------------------------------------------------------------------
- /**
- find a memory pool with the given name; return null if no such pool exists,
- return null. note that this function isn't particularly fast (it just does
- a linear search), so you should probably cache the result.
- */
- MemoryPool *MemoryPoolFactory::findMemoryPool(const char *poolName)
- {
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- if (!strcmp(poolName, pool->getPoolName()))
- {
- DEBUG_ASSERTCRASH(poolName == pool->getPoolName(), ("hmm, ptrs should probably match here"));
- return pool;
- }
- }
- return NULL;
- }
- //-----------------------------------------------------------------------------
- /**
- destroy the given memory pool. you normally will never need to call this directly.
- */
- void MemoryPoolFactory::destroyMemoryPool(MemoryPool *pMemoryPool)
- {
- if (!pMemoryPool)
- return;
- DEBUG_ASSERTCRASH(pMemoryPool->getUsedBlockCount() == 0, ("destroying a nonempty pool"));
- pMemoryPool->removeFromList(&m_firstPoolInFactory);
- // this is evil... since there is no 'placement delete' we must do this the hard way
- // and call the dtor directly. ordinarily this is heinous, but in this case we'll
- // make an exception.
- pMemoryPool->~MemoryPool();
- ::sysFree((void *)pMemoryPool);
- }
- //-----------------------------------------------------------------------------
- /**
- create a new dynamicMemoryAllocator. You normally will never need to call this directly.
- */
- DynamicMemoryAllocator *MemoryPoolFactory::createDynamicMemoryAllocator(Int numSubPools, const PoolInitRec pParms[])
- {
- DynamicMemoryAllocator *dma;
- dma = new (::sysAllocate(sizeof DynamicMemoryAllocator)) DynamicMemoryAllocator; // will throw on failure
- dma->init(this, numSubPools, pParms); // will throw on failure
- dma->addToList(&m_firstDmaInFactory);
- return dma;
- }
- //-----------------------------------------------------------------------------
- /**
- destroy the given dynamicMemoryAllocator. You normally will never need to call this directly.
- */
- void MemoryPoolFactory::destroyDynamicMemoryAllocator(DynamicMemoryAllocator *dma)
- {
- if (!dma)
- return;
- dma->removeFromList(&m_firstDmaInFactory);
- // this is evil... since there is no 'placement delete' we must do this the hard way
- // and call the dtor directly. ordinarily this is heinous, but in this case we'll
- // make an exception.
- dma->~DynamicMemoryAllocator();
- ::sysFree((void *)dma);
- }
- //-----------------------------------------------------------------------------
- /**
- throw away everything in all pools/dmas owned by this factory, but keep the factory
- and pools/dmas themselves valid.
- */
- void MemoryPoolFactory::reset()
- {
- #ifdef MEMORYPOOL_CHECKPOINTING
- debugResetCheckpoints();
- #endif
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- pool->reset();
- }
- for (DynamicMemoryAllocator *dma = m_firstDmaInFactory; dma; dma = dma->getNextDmaInList())
- {
- dma->reset();
- }
- #ifdef MEMORYPOOL_DEBUG
- m_usedBytes = 0;
- m_physBytes = 0;
- m_peakUsedBytes = 0;
- m_peakPhysBytes = 0;
- for (int i = 0; i < MAX_SPECIAL_USED; ++i)
- {
- m_usedBytesSpecial[i] = 0;
- m_usedBytesSpecialPeak[i] = 0;
- m_physBytesSpecial[i] = 0;
- m_physBytesSpecialPeak[i] = 0;
- }
- #ifdef USE_FILLER_VALUE
- calcFillerValue(GameClientRandomValue(0, MAX_INIT_FILLER_COUNT-1));
- #endif
- #endif
- }
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- static const char* s_specialPrefixes[MAX_SPECIAL_USED] =
- {
- "Misc", // the catchall for stuff that doesn't match others
- "W3D_",
- "W3A_",
- "STL_",
- "STR_",
- NULL
- };
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- perform bookkeeping on memory usage statistics.
- */
- void MemoryPoolFactory::adjustTotals(const char* tagString, Int usedDelta, Int physDelta)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- m_usedBytes += usedDelta;
- m_physBytes += physDelta;
- if (m_peakUsedBytes < m_usedBytes)
- m_peakUsedBytes = m_usedBytes;
- if (m_peakPhysBytes < m_physBytes)
- m_peakPhysBytes = m_physBytes;
- int found = 0; // if no matches found, goes into slot zero
- for (int i = 1; i < MAX_SPECIAL_USED; ++i) // start at 1, not zero
- {
- if (s_specialPrefixes[i] == NULL)
- break;
- if (strncmp(tagString, s_specialPrefixes[i], strlen(s_specialPrefixes[i])) == 0)
- {
- found = i;
- break;
- }
- }
- m_usedBytesSpecial[found] += usedDelta;
- m_physBytesSpecial[found] += physDelta;
- if (m_usedBytesSpecialPeak[found] < m_usedBytesSpecial[found])
- m_usedBytesSpecialPeak[found] = m_usedBytesSpecial[found];
- if (m_physBytesSpecialPeak[found] < m_physBytesSpecial[found])
- m_physBytesSpecialPeak[found] = m_physBytesSpecial[found];
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- void MemoryPoolFactory::debugSetInitFillerIndex(Int index)
- {
- #ifdef USE_FILLER_VALUE
- if (index < 0 || index >= MAX_INIT_FILLER_COUNT)
- index = GameClientRandomValue(0, MAX_INIT_FILLER_COUNT-1);
- calcFillerValue(index);
- #endif
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- perform internal consistency checking on the factory, and all of its
- pools and dmas.
- */
- void MemoryPoolFactory::debugMemoryVerify()
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- Int used = 0, phys = 0;
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- pool->debugMemoryVerifyPool();
- used += pool->getUsedBlockCount()*pool->getAllocationSize();
- phys += pool->getTotalBlockCount()*pool->getAllocationSize();
- }
- for (DynamicMemoryAllocator *dma = m_firstDmaInFactory; dma; dma = dma->getNextDmaInList())
- {
- dma->debugMemoryVerifyDma();
- Int tmp = dma->debugCalcRawBlockBytes(NULL);
- used += tmp;
- phys += tmp;
- }
- DEBUG_ASSERTCRASH(used == m_usedBytes, ("used count mismatch"));
- DEBUG_ASSERTCRASH(phys == m_physBytes, ("phys count mismatch"));
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- return true iff the block was allocated by any of the pools/dmas owned
- by this factory.
- */
- Bool MemoryPoolFactory::debugIsBlockInAnyPool(void *pBlock)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugMemoryVerify();
- #endif
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- if (pool->debugIsBlockInPool(pBlock))
- return true;
- }
- for (DynamicMemoryAllocator *dma = m_firstDmaInFactory; dma; dma = dma->getNextDmaInList())
- {
- if (dma->debugIsBlockInDma(pBlock))
- return true;
- }
- return false;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- return the tagstring for this block. this will never return null; if
- the block is null or invalid, "FREE_SINGLEBLOCK_TAG_STRING" will be returned.
- it is assumed that the block was allocated by this factory.
- */
- const char *MemoryPoolFactory::debugGetBlockTagString(void *pBlockPtr)
- {
- USE_PERF_TIMER(MemoryPoolDebugging)
- if (!pBlockPtr)
- return FREE_SINGLEBLOCK_TAG_STRING;
- #ifdef MEMORYPOOL_INTENSE_VERIFY
- debugMemoryVerify();
- #endif
- if (!debugIsBlockInAnyPool(pBlockPtr))
- {
- DEBUG_CRASH(("block is not in this factory"));
- return FREE_SINGLEBLOCK_TAG_STRING;
- }
- MemoryPoolSingleBlock *block = MemoryPoolSingleBlock::recoverBlockFromUserData(pBlockPtr);
- return block->debugGetLiteralTagString();
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- set a new checkpoint for all future blocks allocated/freed by this factory's pools/dmas.
- */
- Int MemoryPoolFactory::debugSetCheckpoint()
- {
- return ++m_curCheckpoint;
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_CHECKPOINTING
- /**
- throw away all checkpoint info for all pools/dmas.
- */
- void MemoryPoolFactory::debugResetCheckpoints()
- {
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- pool->debugResetCheckpoints();
- }
- for (DynamicMemoryAllocator *dma = m_firstDmaInFactory; dma; dma = dma->getNextDmaInList())
- {
- dma->debugResetCheckpoints();
- }
- m_curCheckpoint = 0;
- }
- #endif
- //-----------------------------------------------------------------------------
- void MemoryPoolFactory::memoryPoolUsageReport( const char* filename, FILE *appendToFileInstead )
- {
- #ifdef MEMORYPOOL_DEBUG
- //USE_PERF_TIMER(MemoryPoolDebugging) skip end-of-run reporting stuff
- FILE* perfStatsFile = NULL;
- Int totalNamedPoolPeak = 0;
- if( !appendToFileInstead )
- {
- char tmp[256];
- strcpy(tmp,filename);
- strcat(tmp,".csv");
- perfStatsFile = fopen(tmp, "w");
- }
- else
- {
- perfStatsFile = appendToFileInstead;
- }
- if (perfStatsFile == NULL)
- {
- DEBUG_CRASH(("could not open/create perf file %s -- is it open in another app?",filename));
- return;
- }
- Int lineIdx = 0;
- MemoryPool *pool = m_firstPoolInFactory;
- #ifdef INTENSE_DMA_BOOKKEEPING
- UsedNPeakMap::const_iterator it = TheUsedNPeakMap.begin();
- #endif
- for (;;)
- {
- Bool keepGoing = false;
- if (pool)
- {
- if (lineIdx == 0)
- {
- fprintf(perfStatsFile, "%s,%d","Unpooled Large Blocks (>1024 bytes)",thePeakLargeBlocks/1024);
- }
- else
- {
- Int sz = pool->getAllocationSize();
- Int initial = pool->getInitialBlockCount()*sz;
- Int peak = pool->getPeakBlockCount()*sz;
- Int waste = initial - peak;
- if (waste < 0) waste = 0;
- fprintf(perfStatsFile, "%s,%d,%d",pool->getPoolName(),peak/1024,waste/1024);
- totalNamedPoolPeak += peak;
- pool = pool->getNextPoolInList();
- }
- keepGoing = true;
- }
- else
- {
- fprintf(perfStatsFile, ",,");
- }
- #ifdef INTENSE_DMA_BOOKKEEPING
- if (it != TheUsedNPeakMap.end())
- {
- Int wastepct = (it->second.peakwaste * 100) / (it->second.peak + it->second.peakwaste);
- fprintf(perfStatsFile, ",,,,%s,%d,%d,%d",it->first,it->second.peak/1024,it->second.peakwaste/1024,wastepct);
- ++it;
- keepGoing = true;
- }
- else
- {
- fprintf(perfStatsFile, ",,,,,,,");
- }
- #endif
-
- if (lineIdx < MAX_SPECIAL_USED && s_specialPrefixes[lineIdx] != NULL)
- {
- fprintf(perfStatsFile, ",,,%s,%d",s_specialPrefixes[lineIdx],m_usedBytesSpecialPeak[lineIdx]/1024);
- keepGoing = true;
- }
- fprintf(perfStatsFile, "\n");
- ++lineIdx;
- if (!keepGoing)
- break;
- }
- fflush(perfStatsFile);
- if( !appendToFileInstead )
- {
- fclose(perfStatsFile);
- }
- #endif
- }
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- /**
- send a memory reports (based on the flags/checkpoints) to the debug log.
- */
- void MemoryPoolFactory::debugMemoryReport(Int flags, Int startCheckpoint, Int endCheckpoint, FILE *fp )
- {
- //USE_PERF_TIMER(MemoryPoolDebugging) skip end-of-run reporting stuff
- Int oldFlags = DebugGetFlags();
- DebugSetFlags(oldFlags & ~DEBUG_FLAG_PREPEND_TIME);
- #ifdef MEMORYPOOL_CHECKPOINTING
- Bool doBlockReport = (flags & _REPORT_CP_ALLOCATED_DONTCARE) != 0 && (flags & _REPORT_CP_FREED_DONTCARE) != 0;
- DEBUG_ASSERTCRASH(startCheckpoint >= 0 && startCheckpoint <= endCheckpoint && endCheckpoint <= m_curCheckpoint, ("bad checkpoints"));
- DEBUG_ASSERTCRASH(((flags & _REPORT_CP_ALLOCATED_DONTCARE) != 0) == ((flags & _REPORT_CP_FREED_DONTCARE) != 0), ("bad flags: must set at both alloc and free flag"));
- #endif
- debugMemoryVerify();
- if (flags & REPORT_FACTORYINFO)
- {
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("Begin Factory Info Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("Bytes in use (logical) = %d\n",m_usedBytes));
- DEBUG_LOG(("Bytes in use (physical) = %d\n",m_physBytes));
- DEBUG_LOG(("PEAK Bytes in use (logical) = %d\n",m_peakUsedBytes));
- DEBUG_LOG(("PEAK Bytes in use (physical) = %d\n",m_peakPhysBytes));
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("End Factory Info Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- if( fp )
- {
- fprintf( fp, "------------------------------------------\n" );
- fprintf( fp, "Begin Factory Info Report\n" );
- fprintf( fp, "------------------------------------------\n" );
- fprintf( fp, "Bytes in use (logical) = %d\n",m_usedBytes );
- fprintf( fp, "Bytes in use (physical) = %d\n",m_physBytes );
- fprintf( fp, "PEAK Bytes in use (logical) = %d\n",m_peakUsedBytes );
- fprintf( fp, "PEAK Bytes in use (physical) = %d\n",m_peakPhysBytes );
- fprintf( fp, "------------------------------------------\n" );
- fprintf( fp, "End Factory Info Report\n" );
- fprintf( fp, "------------------------------------------\n" );
- }
- }
- if (flags & REPORT_POOLINFO)
- {
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("Begin Pool Info Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- if( fp )
- {
- fprintf( fp, "------------------------------------------\n" );
- fprintf( fp, "Begin Pool Info Report\n" );
- fprintf( fp, "------------------------------------------\n" );
- }
- MemoryPool::debugPoolInfoReport( NULL, fp );
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- MemoryPool::debugPoolInfoReport( pool, fp );
- }
- for (DynamicMemoryAllocator *dma = m_firstDmaInFactory; dma; dma = dma->getNextDmaInList())
- {
- dma->debugDmaInfoReport( fp );
- }
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("End Pool Info Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- if( fp )
- {
- fprintf( fp, "------------------------------------------\n" );
- fprintf( fp, "End Pool Info Report\n" );
- fprintf( fp, "------------------------------------------\n" );
- }
- }
- if (flags & REPORT_POOL_OVERFLOW)
- {
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("Begin Pool Overflow Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- if (pool->getPeakBlockCount() > pool->getInitialBlockCount())
- {
- DEBUG_LOG(("*** Pool %s overflowed initial allocation of %d (peak allocation was %d)\n",pool->getPoolName(),pool->getInitialBlockCount(),pool->getPeakBlockCount()));
- }
- }
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("End Pool Overflow Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("Begin Pool Underflow Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- for (pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- Int peak = pool->getPeakBlockCount()*pool->getAllocationSize();
- Int initial = pool->getInitialBlockCount()*pool->getAllocationSize();
- if (peak < initial/2 && (initial - peak) > 4096)
- {
- DEBUG_LOG(("*** Pool %s used less than half its initial allocation of %d (peak allocation was %d, wasted %dk)\n",
- pool->getPoolName(),pool->getInitialBlockCount(),pool->getPeakBlockCount(),(initial - peak)/1024));
- }
- }
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("End Pool Underflow Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- }
- if( flags & REPORT_SIMPLE_LEAKS )
- {
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("Begin Simple Leak Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- Int any = 0;
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- any += pool->debugPoolReportLeaks( pool->getPoolName() );
- }
- for (DynamicMemoryAllocator *dma = m_firstDmaInFactory; dma; dma = dma->getNextDmaInList())
- {
- any += dma->debugDmaReportLeaks();
- }
- DEBUG_ASSERTCRASH(!any, ("There were %d memory leaks. Please fix them.\n",any));
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("End Simple Leak Report\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- }
- #ifdef MEMORYPOOL_CHECKPOINTING
- if (doBlockReport)
- {
- const char* nm = (this == TheMemoryPoolFactory) ? "TheMemoryPoolFactory" : "*** UNKNOWN *** MemoryPoolFactory";
- DEBUG_LOG(("\n"));
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("Begin Block Report for %s\n", nm));
- DEBUG_LOG(("------------------------------------------\n"));
- char buf[256] = "";
- if (flags & _REPORT_CP_ALLOCATED_BEFORE) strcat(buf, "AllocBefore ");
- if (flags & _REPORT_CP_ALLOCATED_BETWEEN) strcat(buf, "AllocBetween ");
- if (flags & _REPORT_CP_FREED_BEFORE) strcat(buf, "FreedBefore ");
- if (flags & _REPORT_CP_FREED_BETWEEN) strcat(buf, "FreedBetween ");
- if (flags & _REPORT_CP_FREED_NEVER) strcat(buf, "StillExisting ");
- DEBUG_LOG(("Options: Between checkpoints %d and %d, report on (%s)\n",startCheckpoint,endCheckpoint,buf));
- DEBUG_LOG(("------------------------------------------\n"));
- BlockCheckpointInfo::doBlockCheckpointReport( NULL, "", 0, 0, 0 );
- for (MemoryPool *pool = m_firstPoolInFactory; pool; pool = pool->getNextPoolInList())
- {
- pool->debugCheckpointReport(flags, startCheckpoint, endCheckpoint, pool->getPoolName());
- }
- for (DynamicMemoryAllocator *dma = m_firstDmaInFactory; dma; dma = dma->getNextDmaInList())
- {
- dma->debugCheckpointReport(flags, startCheckpoint, endCheckpoint, "(Oversized)");
- }
- DEBUG_LOG(("------------------------------------------\n"));
- DEBUG_LOG(("End Block Report for %s\n", nm));
- DEBUG_LOG(("------------------------------------------\n"));
- }
- #endif
- DebugSetFlags(oldFlags);
- }
- #endif
- //-----------------------------------------------------------------------------
- // GLOBAL FUNCTIONS
- //-----------------------------------------------------------------------------
- /*
- This is a trick that is intended to force MSVC to link this file (and thus,
- these definitions of new/delete) ahead of all others. (We do debug checking
- to ensure that's the case)
- */
- #if defined(_DEBUG)
- #pragma comment(lib, "GameEngineDebug")
- #elif defined(_INTERNAL)
- #pragma comment(lib, "GameEngineInternal")
- #else
- #pragma comment(lib, "GameEngine")
- #endif
- #ifdef MEMORYPOOL_OVERRIDE_MALLOC
- #pragma comment(linker, "/force:multiple")
- #endif
- static int theLinkTester = 0;
- //-----------------------------------------------------------------------------
- void* STLSpecialAlloc::allocate(size_t __n)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator new"));
- return TheDynamicMemoryAllocator->allocateBytes(__n, "STL_");
- }
- //-----------------------------------------------------------------------------
- void STLSpecialAlloc::deallocate(void* __p, size_t)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator new"));
- TheDynamicMemoryAllocator->freeBytes(__p);
- }
- //-----------------------------------------------------------------------------
- /**
- overload for global operator new; send requests to TheDynamicMemoryAllocator.
- */
- void *operator new(size_t size)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator new"));
- return TheDynamicMemoryAllocator->allocateBytes(size, "global operator new");
- }
- //-----------------------------------------------------------------------------
- /**
- overload for global operator new[]; send requests to TheDynamicMemoryAllocator.
- */
- void *operator new[](size_t size)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator new"));
- return TheDynamicMemoryAllocator->allocateBytes(size, "global operator new[]");
- }
-
- //-----------------------------------------------------------------------------
- /**
- overload for global operator delete; send requests to TheDynamicMemoryAllocator.
- */
- void operator delete(void *p)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator delete"));
- TheDynamicMemoryAllocator->freeBytes(p);
- }
- //-----------------------------------------------------------------------------
- /**
- overload for global operator delete[]; send requests to TheDynamicMemoryAllocator.
- */
- void operator delete[](void *p)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator delete"));
- TheDynamicMemoryAllocator->freeBytes(p);
- }
- //-----------------------------------------------------------------------------
- /**
- overload for global operator new (MFC debug version); send requests to TheDynamicMemoryAllocator.
- */
- void* operator new(size_t size, const char * fname, int)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator new"));
- #ifdef MEMORYPOOL_DEBUG
- return TheDynamicMemoryAllocator->allocateBytesImplementation(size, fname);
- #else
- return TheDynamicMemoryAllocator->allocateBytesImplementation(size);
- #endif
- }
- //-----------------------------------------------------------------------------
- /**
- overload for global operator delete (MFC debug version); send requests to TheDynamicMemoryAllocator.
- */
- void operator delete(void * p, const char *, int)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator delete"));
- TheDynamicMemoryAllocator->freeBytes(p);
- }
- //-----------------------------------------------------------------------------
- /**
- overload for global operator new (MFC debug version); send requests to TheDynamicMemoryAllocator.
- */
- void* operator new[](size_t size, const char * fname, int)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator new"));
- #ifdef MEMORYPOOL_DEBUG
- return TheDynamicMemoryAllocator->allocateBytesImplementation(size, fname);
- #else
- return TheDynamicMemoryAllocator->allocateBytesImplementation(size);
- #endif
- }
- //-----------------------------------------------------------------------------
- /**
- overload for global operator delete (MFC debug version); send requests to TheDynamicMemoryAllocator.
- */
- void operator delete[](void * p, const char *, int)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager before calling global operator delete"));
- TheDynamicMemoryAllocator->freeBytes(p);
- }
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_OVERRIDE_MALLOC
- void *calloc(size_t a, size_t b)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager"));
- return TheDynamicMemoryAllocator->allocateBytes(a * b, "calloc");
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_OVERRIDE_MALLOC
- void free(void * p)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager"));
- TheDynamicMemoryAllocator->freeBytes(p);
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_OVERRIDE_MALLOC
- void *malloc(size_t a)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- DEBUG_ASSERTCRASH(TheDynamicMemoryAllocator != NULL, ("must init memory manager"));
- return TheDynamicMemoryAllocator->allocateBytesDoNotZero(a, "malloc");
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_OVERRIDE_MALLOC
- void *realloc(void *p, size_t s)
- {
- DEBUG_CRASH(("realloc is evil. do not call it."));
- throw ERROR_OUT_OF_MEMORY;
- }
- #endif
- //-----------------------------------------------------------------------------
- /**
- Initialize the memory manager, and create TheMemoryPoolFactory and TheDynamicMemoryAllocator.
- */
- void initMemoryManager()
- {
- if (TheMemoryPoolFactory == NULL)
- {
- Int numSubPools;
- const PoolInitRec *pParms;
- userMemoryManagerGetDmaParms(&numSubPools, &pParms);
- TheMemoryPoolFactory = new (::sysAllocate(sizeof MemoryPoolFactory)) MemoryPoolFactory; // will throw on failure
- TheMemoryPoolFactory->init(); // will throw on failure
- TheDynamicMemoryAllocator = TheMemoryPoolFactory->createDynamicMemoryAllocator(numSubPools, pParms); // will throw on failure
- userMemoryManagerInitPools();
- thePreMainInitFlag = false;
- }
- else
- {
- if (thePreMainInitFlag)
- {
- // quietly ignore the call
- }
- else
- {
- DEBUG_CRASH(("memory manager is already inited"));
- }
- }
- char* linktest;
-
- theLinkTester = 0;
- linktest = new char;
- delete linktest;
- linktest = new char[8];
- delete [] linktest;
- linktest = new char("",1);
- delete linktest;
- #ifdef MEMORYPOOL_OVERRIDE_MALLOC
- linktest = (char*)malloc(1);
- free(linktest);
- linktest = (char*)calloc(1,1);
- free(linktest);
- #endif
- #ifdef MEMORYPOOL_OVERRIDE_MALLOC
- if (theLinkTester != 10)
- #else
- if (theLinkTester != 6)
- #endif
- {
- DEBUG_CRASH(("Wrong operator new/delete linked in! Fix this...\n"));
- exit(-1);
- }
- theMainInitFlag = true;
- }
- //-----------------------------------------------------------------------------
- Bool isMemoryManagerOfficiallyInited()
- {
- return theMainInitFlag;
- }
- //-----------------------------------------------------------------------------
- /**
- Initialize the memory manager, and create TheMemoryPoolFactory and TheDynamicMemoryAllocator.
- This is only called if memory is allocated prior to the normal call to initMemoryManager
- (generally via a static C++ ctor).
- */
- static void preMainInitMemoryManager()
- {
- if (TheMemoryPoolFactory == NULL)
- {
- DEBUG_INIT(DEBUG_FLAGS_DEFAULT);
- DEBUG_LOG(("*** Initing Memory Manager prior to main!\n"));
- Int numSubPools;
- const PoolInitRec *pParms;
- userMemoryManagerGetDmaParms(&numSubPools, &pParms);
- TheMemoryPoolFactory = new (::sysAllocate(sizeof MemoryPoolFactory)) MemoryPoolFactory; // will throw on failure
- TheMemoryPoolFactory->init(); // will throw on failure
- TheDynamicMemoryAllocator = TheMemoryPoolFactory->createDynamicMemoryAllocator(numSubPools, pParms); // will throw on failure
- userMemoryManagerInitPools();
- thePreMainInitFlag = true;
- }
- }
- //-----------------------------------------------------------------------------
- /**
- shutdown the memory manager and discard all memory. Note: if preMainInitMemoryManager()
- was called prior to initMemoryManager(), this call will do nothing.
- */
- void shutdownMemoryManager()
- {
- if (thePreMainInitFlag)
- {
- #ifdef MEMORYPOOL_DEBUG
- DEBUG_LOG(("*** Memory Manager was inited prior to main -- skipping shutdown!\n"));
- #endif
- }
- else
- {
- if (TheDynamicMemoryAllocator)
- {
- DEBUG_ASSERTCRASH(TheMemoryPoolFactory, ("hmm, no factory"));
- if (TheMemoryPoolFactory)
- TheMemoryPoolFactory->destroyDynamicMemoryAllocator(TheDynamicMemoryAllocator);
- TheDynamicMemoryAllocator = NULL;
- }
- if (TheMemoryPoolFactory)
- {
- // this is evil... since there is no 'placement delete' we must do this the hard way
- // and call the dtor directly. ordinarily this is heinous, but in this case we'll
- // make an exception.
- TheMemoryPoolFactory->~MemoryPoolFactory();
- ::sysFree((void *)TheMemoryPoolFactory);
- TheMemoryPoolFactory = NULL;
- }
- #ifdef MEMORYPOOL_DEBUG
- DEBUG_LOG(("Peak system allocation was %d bytes\n",thePeakSystemAllocationInBytes));
- DEBUG_LOG(("Wasted DMA space (peak) was %d bytes\n",thePeakWastedDMA));
- DEBUG_ASSERTCRASH(theTotalSystemAllocationInBytes == 0, ("Leaked a total of %d raw bytes\n", theTotalSystemAllocationInBytes));
- #endif
- }
- theMainInitFlag = false;
- }
- //-----------------------------------------------------------------------------
- void* createW3DMemPool(const char *poolName, int allocationSize)
- {
- ++theLinkTester;
- preMainInitMemoryManager();
- MemoryPool* pool = TheMemoryPoolFactory->createMemoryPool(poolName, allocationSize, 0, 0);
- DEBUG_ASSERTCRASH(pool && pool->getAllocationSize() == allocationSize, ("bad w3d pool"));
- return pool;
- }
- //-----------------------------------------------------------------------------
- void* allocateFromW3DMemPool(void* pool, int allocationSize)
- {
- DEBUG_ASSERTCRASH(pool, ("pool is null\n"));
- DEBUG_ASSERTCRASH(pool && ((MemoryPool*)pool)->getAllocationSize() == allocationSize, ("bad w3d pool size %s",((MemoryPool*)pool)->getPoolName()));
- return ((MemoryPool*)pool)->allocateBlock("allocateFromW3DMemPool");
- }
- //-----------------------------------------------------------------------------
- void* allocateFromW3DMemPool(void* pool, int allocationSize, const char* msg, int unused)
- {
- DEBUG_ASSERTCRASH(pool, ("pool is null\n"));
- DEBUG_ASSERTCRASH(pool && ((MemoryPool*)pool)->getAllocationSize() == allocationSize, ("bad w3d pool size %s",((MemoryPool*)pool)->getPoolName()));
- return ((MemoryPool*)pool)->allocateBlock(msg);
- }
- //-----------------------------------------------------------------------------
- void freeFromW3DMemPool(void* pool, void* p)
- {
- DEBUG_ASSERTCRASH(pool, ("pool is null\n"));
- ((MemoryPool*)pool)->freeBlock(p);
- }
|