FastMM4.pas 393 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058
  1. (*
  2. Fast Memory Manager 4.98
  3. Description:
  4. A fast replacement memory manager for Embarcadero Delphi Win32 applications
  5. that scales well under multi-threaded usage, is not prone to memory
  6. fragmentation, and supports shared memory without the use of external .DLL
  7. files.
  8. Homepage:
  9. http://fastmm.sourceforge.net
  10. Advantages:
  11. - Fast
  12. - Low overhead. FastMM is designed for an average of 5% and maximum of 10%
  13. overhead per block.
  14. - Supports up to 3GB of user mode address space under Windows 32-bit and 4GB
  15. under Windows 64-bit. Add the "$SetPEFlags $20" option (in curly braces)
  16. to your .dpr to enable this.
  17. - Highly aligned memory blocks. Can be configured for either 8-byte or 16-byte
  18. alignment.
  19. - Good scaling under multi-threaded applications
  20. - Intelligent reallocations. Avoids slow memory move operations through
  21. not performing unneccesary downsizes and by having a minimum percentage
  22. block size growth factor when an in-place block upsize is not possible.
  23. - Resistant to address space fragmentation
  24. - No external DLL required when sharing memory between the application and
  25. external libraries (provided both use this memory manager)
  26. - Optionally reports memory leaks on program shutdown. (This check can be set
  27. to be performed only if Delphi is currently running on the machine, so end
  28. users won't be bothered by the error message.)
  29. - Supports Delphi 4 (or later), C++ Builder 4 (or later), Kylix 3.
  30. Usage:
  31. Delphi:
  32. Place this unit as the very first unit under the "uses" section in your
  33. project's .dpr file. When sharing memory between an application and a DLL
  34. (e.g. when passing a long string or dynamic array to a DLL function), both the
  35. main application and the DLL must be compiled using this memory manager (with
  36. the required conditional defines set). There are some conditional defines
  37. (inside FastMM4Options.inc) that may be used to tweak the memory manager. To
  38. enable support for a user mode address space greater than 2GB you will have to
  39. use the EditBin* tool to set the LARGE_ADDRESS_AWARE flag in the EXE header.
  40. This informs Windows x64 or Windows 32-bit (with the /3GB option set) that the
  41. application supports an address space larger than 2GB (up to 4GB). In Delphi 6
  42. and later you can also specify this flag through the compiler directive
  43. {$SetPEFlags $20}
  44. *The EditBin tool ships with the MS Visual C compiler.
  45. C++ Builder 6:
  46. Refer to the instructions inside FastMM4BCB.cpp.
  47. License:
  48. This work is copyright Professional Software Development / Pierre le Riche. It
  49. is released under a dual license, and you may choose to use it under either the
  50. Mozilla Public License 1.1 (MPL 1.1, available from
  51. http://www.mozilla.org/MPL/MPL-1.1.html) or the GNU Lesser General Public
  52. License 2.1 (LGPL 2.1, available from
  53. http://www.opensource.org/licenses/lgpl-license.php). If you find FastMM useful
  54. or you would like to support further development, a donation would be much
  55. appreciated. My banking details are:
  56. Country: South Africa
  57. Bank: ABSA Bank Ltd
  58. Branch: Somerset West
  59. Branch Code: 334-712
  60. Account Name: PSD (Distribution)
  61. Account No.: 4041827693
  62. Swift Code: ABSAZAJJ
  63. My PayPal account is:
  64. bof@psd.co.za
  65. Contact Details:
  66. My contact details are shown below if you would like to get in touch with me.
  67. If you use this memory manager I would like to hear from you: please e-mail me
  68. your comments - good and bad.
  69. Snailmail:
  70. PO Box 2514
  71. Somerset West
  72. 7129
  73. South Africa
  74. E-mail:
  75. plr@psd.co.za
  76. Support:
  77. If you have trouble using FastMM, you are welcome to drop me an e-mail at the
  78. address above, or you may post your questions in the BASM newsgroup on the
  79. Embarcadero news server (which is where I hang out quite frequently).
  80. Disclaimer:
  81. FastMM has been tested extensively with both single and multithreaded
  82. applications on various hardware platforms, but unfortunately I am not in a
  83. position to make any guarantees. Use it at your own risk.
  84. Acknowledgements (for version 4):
  85. - Eric Grange for his RecyclerMM on which the earlier versions of FastMM were
  86. based. RecyclerMM was what inspired me to try and write my own memory
  87. manager back in early 2004.
  88. - Primoz Gabrijelcic for helping to track down various bugs.
  89. - Dennis Christensen for his tireless efforts with the Fastcode project:
  90. helping to develop, optimize and debug the growing Fastcode library.
  91. - JiYuan Xie for implementing the leak reporting code for C++ Builder.
  92. - Pierre Y. for his suggestions regarding the extension of the memory leak
  93. checking options.
  94. - Hanspeter Widmer for his suggestion to have an option to display install and
  95. uninstall debug messages and moving options to a separate file, as well as
  96. the new usage tracker.
  97. - Anders Isaksson and Greg for finding and identifying the "DelphiIsRunning"
  98. bug under Delphi 5.
  99. - Francois Malan for various suggestions and bug reports.
  100. - Craig Peterson for helping me identify the cache associativity issues that
  101. could arise due to medium blocks always being an exact multiple of 256 bytes.
  102. Also for various other bug reports and enhancement suggestions.
  103. - Jarek Karciarz, Vladimir Ulchenko (Vavan) and Bob Gonder for their help in
  104. implementing the BCB support.
  105. - Ben Taylor for his suggestion to display the object class of all memory
  106. leaks.
  107. - Jean Marc Eber and Vincent Mahon (the Memcheck guys) for the call stack
  108. trace code and also the method used to catch virtual method calls on freed
  109. objects.
  110. - Nahan Hyn for the suggestion to be able to enable or disable memory leak
  111. reporting through a global variable (the "ManualLeakReportingControl"
  112. option.)
  113. - Leonel Togniolli for various suggestions with regard to enhancing the bug
  114. tracking features of FastMM and other helpful advice.
  115. - Joe Bain and Leonel Togniolli for the workaround to QC#10922 affecting
  116. compilation under Delphi 2005.
  117. - Robert Marquardt for the suggestion to make localisation of FastMM easier by
  118. having all string constants together.
  119. - Simon Kissel and Fikret Hasovic for their help in implementing Kylix support.
  120. - Matthias Thoma, Petr Vones, Robert Rossmair and the rest of the JCL team for
  121. their debug info library used in the debug info support DLL and also the
  122. code used to check for a valid call site in the "raw" stack trace code.
  123. - Andreas Hausladen for the suggestion to use an external DLL to enable the
  124. reporting of debug information.
  125. - Alexander Tabakov for various good suggestions regarding the debugging
  126. facilities of FastMM.
  127. - M. Skloff for some useful suggestions and bringing to my attention some
  128. compiler warnings.
  129. - Martin Aignesberger for the code to use madExcept instead of the JCL library
  130. inside the debug info support DLL.
  131. - Diederik and Dennis Passmore for the suggestion to be able to register
  132. expected leaks.
  133. - Dario Tiraboschi and Mark Gebauer for pointing out the problems that occur
  134. when range checking and complete boolean evaluation is turned on.
  135. - Arthur Hoornweg for notifying me of the image base being incorrect for
  136. borlndmm.dll.
  137. - Theo Carr-Brion and Hanspeter Widmer for finding the false alarm error
  138. message "Block Header Has Been Corrupted" bug in FullDebugMode.
  139. - Danny Heijl for reporting the compiler error in "release" mode.
  140. - Omar Zelaya for reporting the BCB support regression bug.
  141. - Dan Miser for various good suggestions, e.g. not logging expected leaks to
  142. file, enhancements the stack trace and messagebox functionality, etc.
  143. - Arjen de Ruijter for fixing the bug in GetMemoryLeakType that caused it
  144. to not properly detect expected leaks registered by class when in
  145. "FullDebugMode".
  146. - Aleksander Oven for reporting the installation problem when trying to use
  147. FastMM in an application together with libraries that all use runtime
  148. packages.
  149. - Kristofer Skaug for reporting the bug that sometimes causes the leak report
  150. to be shown, even when all the leaks have been registered as expected leaks.
  151. Also for some useful enhancement suggestions.
  152. - Günther Schoch for the "RequireDebuggerPresenceForLeakReporting" option.
  153. - Jan Schlüter for the "ForceMMX" option.
  154. - Hallvard Vassbotn for various good enhancement suggestions.
  155. - Mark Edington for some good suggestions and bug reports.
  156. - Paul Ishenin for reporting the compilation error when the NoMessageBoxes
  157. option is set and also the missing call stack entries issue when "raw" stack
  158. traces are enabled, as well as for the Russian translation.
  159. - Cristian Nicola for reporting the compilation bug when the
  160. CatchUseOfFreedInterfaces option was enabled (4.40).
  161. - Mathias Rauen (madshi) for improving the support for madExcept in the debug
  162. info support DLL.
  163. - Roddy Pratt for the BCB5 support code.
  164. - Rene Mihula for the Czech translation and the suggestion to have dynamic
  165. loading of the FullDebugMode DLL as an option.
  166. - Artur Redzko for the Polish translation.
  167. - Bart van der Werf for helping me solve the DLL unload order problem when
  168. using the debug mode borlndmm.dll library, as well as various other
  169. suggestions.
  170. - JRG ("The Delphi Guy") for the Spanish translation.
  171. - Justus Janssen for Delphi 4 support.
  172. - Vadim Lopushansky and Charles Vinal for reporting the Delphi 5 compiler
  173. error in version 4.50.
  174. - Johni Jeferson Capeletto for the Brazilian Portuguese translation.
  175. - Kurt Fitzner for reporting the BCb6 compiler error in 4.52.
  176. - Michal Niklas for reporting the Kylix compiler error in 4.54.
  177. - Thomas Speck and Uwe Queisser for German translations.
  178. - Zaenal Mutaqin for the Indonesian translation.
  179. - Carlos Macao for the Portuguese translation.
  180. - Michael Winter for catching the performance issue when reallocating certain
  181. block sizes.
  182. - dzmitry[li] for the Belarussian translation.
  183. - Marcelo Montenegro for the updated Spanish translation.
  184. - Jud Cole for finding and reporting the bug which may trigger a read access
  185. violation when upsizing certain small block sizes together with the
  186. "UseCustomVariableSizeMoveRoutines" option.
  187. - Zdenek Vasku for reporting and fixing the memory manager sharing bug
  188. affecting Windows 95/98/Me.
  189. - RB Winston for suggesting the improvement to GExperts "backup" support.
  190. - Thomas Schulz for reporting the bug affecting large address space support
  191. under FullDebugMode, as well as the recursive call bug when attempting to
  192. report memory leaks when EnableMemoryLeakReporting is disabled.
  193. - Luigi Sandon for the Italian translation.
  194. - Werner Bochtler for various suggestions and bug reports.
  195. - Markus Beth for suggesting the "NeverSleepOnThreadContention" option.
  196. - JiYuan Xie for the Simplified Chinese translation.
  197. - Andrey Shtukaturov for the updated Russian translation, as well as the
  198. Ukrainian translation.
  199. - Dimitry Timokhov for finding two elusive bugs in the memory leak class
  200. detection code.
  201. - Paulo Moreno for fixing the AllocMem bug in FullDebugMode that prevented
  202. large blocks from being cleared.
  203. - Vladimir Bochkarev for the suggestion to remove some unnecessary code if the
  204. MM sharing mechanism is disabled.
  205. - Loris Luise for the version constant suggestion.
  206. - J.W. de Bokx for the MessageBox bugfix.
  207. - Igor Lindunen for reporting the bug that caused the Align16Bytes option to
  208. not work in FullDebugMode.
  209. - Ionut Muntean for the Romanian translation.
  210. - Florent Ouchet for the French translation.
  211. - Marcus Mönnig for the ScanMemoryPoolForCorruptions suggestion and the
  212. suggestion to have the option to scan the memory pool before every
  213. operation when in FullDebugMode.
  214. - Francois Piette for bringing under my attention that
  215. ScanMemoryPoolForCorruption was not thread safe.
  216. - Michael Rabatscher for reporting some compiler warnings.
  217. - QianYuan Wang for the Simplified Chinese translation of FastMM4Options.inc.
  218. - Maurizio Lotauro and Christian-W. Budde for reporting some Delphi 5
  219. compiler errors.
  220. - Patrick van Logchem for the DisableLoggingOfMemoryDumps option.
  221. - Norbert Spiegel for the BCB4 support code.
  222. - Uwe Schuster for the improved string leak detection code.
  223. - Murray McGowan for improvements to the usage tracker.
  224. - Michael Hieke for the SuppressFreeMemErrorsInsideException option as well
  225. as a bugfix to GetMemoryMap.
  226. - Richard Bradbrook for fixing the Windows 95 FullDebugMode support that was
  227. broken in version 4.94.
  228. - Zach Saw for the suggestion to (optionally) use SwitchToThread when
  229. waiting for a lock on a shared resource to be released.
  230. - Everyone who have made donations. Thanks!
  231. - Any other Fastcoders or supporters that I have forgotten, and also everyone
  232. that helped with the older versions.
  233. Change log:
  234. Version 1.00 (28 June 2004):
  235. - First version (called PSDMemoryManager). Based on RecyclerMM (free block
  236. stack approach) by Eric Grange.
  237. Version 2.00 (3 November 2004):
  238. - Complete redesign and rewrite from scratch. Name changed to FastMM to
  239. reflect this fact. Uses a linked-list approach. Is faster, has less memory
  240. overhead, and will now catch most bad pointers on FreeMem calls.
  241. Version 3.00 (1 March 2005):
  242. - Another rewrite. Reduced the memory overhead by: (a) not having a separate
  243. memory area for the linked list of free blocks (uses space inside free
  244. blocks themselves) (b) batch managers are allocated as part of chunks (c)
  245. block size lookup table size reduced. This should make FastMM more CPU
  246. cache friendly.
  247. Version 4.00 (7 June 2005):
  248. - Yet another rewrite. FastMM4 is in fact three memory managers in one: Small
  249. blocks (up to a few KB) are managed through the binning model in the same
  250. way as previous versions, medium blocks (from a few KB up to approximately
  251. 256K) are allocated in a linked-list fashion, and large blocks are grabbed
  252. directly from the system through VirtualAlloc. This 3-layered design allows
  253. very fast operation with the most frequently used block sizes (small
  254. blocks), while also minimizing fragmentation and imparting significant
  255. overhead savings with blocks larger than a few KB.
  256. Version 4.01 (8 June 2005):
  257. - Added the options "RequireDebugInfoForLeakReporting" and
  258. "RequireIDEPresenceForLeakReporting" as suggested by Pierre Y.
  259. - Fixed the "DelphiIsRunning" function not working under Delphi 5, and
  260. consequently no leak checking. (Reported by Anders Isaksson and Greg.)
  261. Version 4.02 (8 June 2005):
  262. - Fixed the compilation error when both the "AssumeMultiThreaded" and
  263. "CheckHeapForCorruption options were set. (Reported by Francois Malan.)
  264. Version 4.03 (9 June 2005):
  265. - Added descriptive error messages when FastMM4 cannot be installed because
  266. another MM has already been installed or memory has already been allocated.
  267. Version 4.04 (13 June 2005):
  268. - Added a small fixed offset to the size of medium blocks (previously always
  269. exact multiples of 256 bytes). This makes performance problems due to CPU
  270. cache associativity limitations much less likely. (Reported by Craig
  271. Peterson.)
  272. Version 4.05 (17 June 2005):
  273. - Added the Align16Bytes option. Disable this option to drop the 16 byte
  274. alignment restriction and reduce alignment to 8 bytes for the smallest
  275. block sizes. Disabling Align16Bytes should lower memory consumption at the
  276. cost of complicating the use of aligned SSE move instructions. (Suggested
  277. by Craig Peterson.)
  278. - Added a support unit for C++ Builder 6 - Add FastMM4BCB.cpp and
  279. FastMM4.pas to your BCB project to use FastMM instead of the RTL MM. Memory
  280. leak checking is not supported because (unfortunately) once an MM is
  281. installed under BCB you cannot uninstall it... at least not without
  282. modifying the RTL code in exit.c or patching the RTL code runtime. (Thanks
  283. to Jarek Karciarz, Vladimir Ulchenko and Bob Gonder.)
  284. Version 4.06 (22 June 2005):
  285. - Displays the class of all leaked objects on the memory leak report and also
  286. tries to identify leaked long strings. Previously it only displayed the
  287. sizes of all leaked blocks. (Suggested by Ben Taylor.)
  288. - Added support for displaying the sizes of medium and large block memory
  289. leaks. Previously it only displayed details for small block leaks.
  290. Version 4.07 (22 June 2005):
  291. - Fixed the detection of the class of leaked objects not working under
  292. Windows 98/Me.
  293. Version 4.08 (27 June 2005):
  294. - Added a BorlndMM.dpr project to allow you to build a borlndmm.dll that uses
  295. FastMM4 instead of the default memory manager. You may replace the old
  296. DLL in the Delphi \Bin directory to make the IDE use this memory manager
  297. instead.
  298. Version 4.09 (30 June 2005):
  299. - Included a patch fix for the bug affecting replacement borlndmm.dll files
  300. with Delphi 2005 (QC#14007). Compile the patch, close Delphi, and run it
  301. once to patch your vclide90.bpl. You will now be able to use the
  302. replacement borlndmm.dll to speed up the Delphi 2005 IDE as well.
  303. Version 4.10 (7 July 2005):
  304. - Due to QC#14070 ("Delphi IDE attempts to free memory after the shutdown
  305. code of borlndmm.dll has been called"), FastMM cannot be uninstalled
  306. safely when used inside a replacement borlndmm.dll for the IDE. Added a
  307. conditional define "NeverUninstall" for this purpose.
  308. - Added the "FullDebugMode" option to pad all blocks with a header and footer
  309. to help you catch memory overwrite bugs in your applications. All blocks
  310. returned to freemem are also zeroed out to help catch bugs involving the
  311. use of previously freed blocks. Also catches attempts at calling virtual
  312. methods of freed objects provided the block in question has not been reused
  313. since the object was freed. Displays stack traces on error to aid debugging.
  314. - Added the "LogErrorsToFile" option to log all errors to a text file in the
  315. same folder as the application.
  316. - Added the "ManualLeakReportingControl" option (suggested by Nahan Hyn) to
  317. enable control over whether the memory leak report should be done or not
  318. via a global variable.
  319. Version 4.11 (7 July 2005):
  320. - Fixed a compilation error under Delphi 2005 due to QC#10922. (Thanks to Joe
  321. Bain and Leonel Togniolli.)
  322. - Fixed leaked object classes not displaying in the leak report in
  323. "FullDebugMode".
  324. Version 4.12 (8 July 2005):
  325. - Moved all the string constants to one place to make it easier to do
  326. translations into other languages. (Thanks to Robert Marquardt.)
  327. - Added support for Kylix. Some functionality is currently missing: No
  328. support for detecting the object class on leaks and also no MM sharing.
  329. (Thanks to Simon Kissel and Fikret Hasovic).
  330. Version 4.13 (11 July 2005):
  331. - Added the FastMM_DebugInfo.dll support library to display debug info for
  332. stack traces.
  333. - Stack traces for the memory leak report is now logged to the log file in
  334. "FullDebugMode".
  335. Version 4.14 (14 July 2005):
  336. - Fixed string leaks not being detected as such in "FullDebugMode". (Thanks
  337. to Leonel Togniolli.)
  338. - Fixed the compilation error in "FullDebugMode" when "LogErrorsToFile" is
  339. not set. (Thanks to Leonel Togniolli.)
  340. - Added a "Release" option to allow the grouping of various options and to
  341. make it easier to make debug and release builds. (Thanks to Alexander
  342. Tabakov.)
  343. - Added a "HideMemoryLeakHintMessage" option to not display the hint below
  344. the memory leak message. (Thanks to Alexander Tabakov.)
  345. - Changed the fill character for "FullDebugMode" from zero to $80 to be able
  346. to differentiate between invalid memory accesses using nil pointers to
  347. invalid memory accesses using fields of freed objects. FastMM tries to
  348. reserve the 64K block starting at $80800000 at startup to ensure that an
  349. A/V will occur when this block is accessed. (Thanks to Alexander Tabakov.)
  350. - Fixed some compiler warnings. (Thanks to M. Skloff)
  351. - Fixed some display bugs in the memory leak report. (Thanks to Leonel
  352. Togniolli.)
  353. - Added a "LogMemoryLeakDetailToFile" option. Some applications leak a lot of
  354. memory and can make the log file grow very large very quickly.
  355. - Added the option to use madExcept instead of the JCL Debug library in the
  356. debug info support DLL. (Thanks to Martin Aignesberger.)
  357. - Added procedures "GetMemoryManagerState" and "GetMemoryMap" to retrieve
  358. statistics about the current state of the memory manager and memory pool.
  359. (A usage tracker form together with a demo is also available.)
  360. Version 4.15 (14 July 2005):
  361. - Fixed a false 4GB(!) memory leak reported in some instances.
  362. Version 4.16 (15 July 2005):
  363. - Added the "CatchUseOfFreedInterfaces" option to catch the use of interfaces
  364. of freed objects. This option is not compatible with checking that a freed
  365. block has not been modified, so enable this option only when hunting an
  366. invalid interface reference. (Only relevant if "FullDebugMode" is set.)
  367. - During shutdown FastMM now checks that all free blocks have not been
  368. modified since being freed. (Only when "FullDebugMode" is set and
  369. "CatchUseOfFreedInterfaces" is disabled.)
  370. Version 4.17 (15 July 2005):
  371. - Added the AddExpectedMemoryLeaks and RemoveExpectedMemoryLeaks procedures to
  372. register/unregister expected leaks, thus preventing the leak report from
  373. displaying if only expected leaks occurred. (Thanks to Diederik and Dennis
  374. Passmore for the suggestion.) (Note: these functions were renamed in later
  375. versions.)
  376. - Fixed the "LogMemoryLeakDetailToFile" not logging memory leak detail to file
  377. as it is supposed to. (Thanks to Leonel Togniolli.)
  378. Version 4.18 (18 July 2005):
  379. - Fixed some issues when range checking or complete boolean evaluation is
  380. switched on. (Thanks to Dario Tiraboschi and Mark Gebauer.)
  381. - Added the "OutputInstallUninstallDebugString" option to display a message when
  382. FastMM is installed or uninstalled. (Thanks to Hanspeter Widmer.)
  383. - Moved the options to a separate include file. (Thanks to Hanspeter Widmer.)
  384. - Moved message strings to a separate file for easy translation.
  385. Version 4.19 (19 July 2005):
  386. - Fixed Kylix support that was broken in 4.14.
  387. Version 4.20 (20 July 2005):
  388. - Fixed a false memory overwrite report at shutdown in "FullDebugMode". If you
  389. consistently got a "Block Header Has Been Corrupted" error message during
  390. shutdown at address $xxxx0070 then it was probably a false alarm. (Thanks to
  391. Theo Carr-Brion and Hanspeter Widmer.}
  392. Version 4.21 (27 July 2005):
  393. - Minor change to the block header flags to make it possible to immediately
  394. tell whether a medium block is being used as a small block pool or not.
  395. (Simplifies the leak checking and status reporting code.)
  396. - Expanded the functionality around the management of expected memory leaks.
  397. - Added the "ClearLogFileOnStartup" option. Deletes the log file during
  398. initialization. (Thanks to M. Skloff.)
  399. - Changed "OutputInstallUninstallDebugString" to use OutputDebugString instead
  400. of MessageBox. (Thanks to Hanspeter Widmer.)
  401. Version 4.22 (1 August 2005):
  402. - Added a FastAllocMem function that avoids an unnecessary FillChar call with
  403. large blocks.
  404. - Changed large block resizing behavior to be a bit more conservative. Large
  405. blocks will be downsized if the new size is less than half of the old size
  406. (the threshold was a quarter previously).
  407. Version 4.23 (6 August 2005):
  408. - Fixed BCB6 support (Thanks to Omar Zelaya).
  409. - Renamed "OutputInstallUninstallDebugString" to "UseOutputDebugString", and
  410. added debug string output on memory leak or error detection.
  411. Version 4.24 (11 August 2005):
  412. - Added the "NoMessageBoxes" option to suppress the display of message boxes,
  413. which is useful for services that should not be interrupted. (Thanks to Dan
  414. Miser).
  415. - Changed the stack trace code to return the line number of the caller and not
  416. the line number of the return address. (Thanks to Dan Miser).
  417. Version 4.25 (15 August 2005):
  418. - Fixed GetMemoryLeakType not detecting expected leaks registered by class
  419. when in "FullDebugMode". (Thanks to Arjen de Ruijter).
  420. Version 4.26 (18 August 2005):
  421. - Added a "UseRuntimePackages" option that allows FastMM to be used in a main
  422. application together with DLLs that all use runtime packages. (Thanks to
  423. Aleksander Oven.)
  424. Version 4.27 (24 August 2005):
  425. - Fixed a bug that sometimes caused the leak report to be shown even though all
  426. leaks were registered as expected leaks. (Thanks to Kristofer Skaug.)
  427. Version 4.29 (30 September 2005):
  428. - Added the "RequireDebuggerPresenceForLeakReporting" option to only display
  429. the leak report if the application is run inside the IDE. (Thanks to Günther
  430. Schoch.)
  431. - Added the "ForceMMX" option, which when disabled will check the CPU for
  432. MMX compatibility before using MMX. (Thanks to Jan Schlüter.)
  433. - Added the module name to the title of error dialogs to more easily identify
  434. which application caused the error. (Thanks to Kristofer Skaug.)
  435. - Added an ASCII dump to the "FullDebugMode" memory dumps. (Thanks to Hallvard
  436. Vassbotn.)
  437. - Added the option "HideExpectedLeaksRegisteredByPointer" to suppress the
  438. display and logging of expected memory leaks that were registered by pointer.
  439. (Thanks to Dan Miser.) Leaks registered by size or class are often ambiguous,
  440. so these expected leaks are always logged to file (in FullDebugMode) and are
  441. never hidden from the leak display (only displayed if there is at least one
  442. unexpected leak).
  443. - Added a procedure "GetRegisteredMemoryLeaks" to return a list of all
  444. registered memory leaks. (Thanks to Dan Miser.)
  445. - Added the "RawStackTraces" option to perform "raw" stack traces, negating
  446. the need for stack frames. This will usually result in more complete stack
  447. traces in FullDebugMode error reports, but it is significantly slower.
  448. (Thanks to Hallvard Vassbotn, Dan Miser and the JCL team.)
  449. Version 4.31 (2 October 2005):
  450. - Fixed the crash bug when both "RawStackTraces" and "FullDebugMode" were
  451. enabled. (Thanks to Dan Miser and Mark Edington.)
  452. Version 4.33 (6 October 2005):
  453. - Added a header corruption check to all memory blocks that are identified as
  454. leaks in FullDebugMode. This allows better differentiation between memory
  455. pool corruption bugs and actual memory leaks.
  456. - Fixed the stack overflow bug when using "RawStackTraces".
  457. Version 4.35 (6 October 2005):
  458. - Fixed a compilation error when the "NoMessageBoxes" option is set. (Thanks
  459. to Paul Ishenin.)
  460. - Before performing a "raw" stack trace, FastMM now checks whether exception
  461. handling is in place. If exception handling is not in place FastMM falls
  462. back to stack frame tracing. (Exception handling is required to handle the
  463. possible A/Vs when reading invalid call addresses. Exception handling is
  464. usually always available except when SysUtils hasn't been initialized yet or
  465. after SysUtils has been finalized.)
  466. Version 4.37 (8 October 2005):
  467. - Fixed the missing call stack trace entry issue when dynamically loading DLLs.
  468. (Thanks to Paul Ishenin.)
  469. Version 4.39 (12 October 2005):
  470. - Restored the performance with "RawStackTraces" enabled back to the level it
  471. was in 4.35.
  472. - Fixed the stack overflow error when using "RawStackTraces" that I thought I
  473. had fixed in 4.31, but unfortunately didn't. (Thanks to Craig Peterson.)
  474. Version 4.40 (13 October 2005):
  475. - Improved "RawStackTraces" to have less incorrect extra entries. (Thanks to
  476. Craig Peterson.)
  477. - Added the Russian (by Paul Ishenin) and Afrikaans translations of
  478. FastMM4Messages.pas.
  479. Version 4.42 (13 October 2005):
  480. - Fixed the compilation error when "CatchUseOfFreedInterfaces" is enabled.
  481. (Thanks to Cristian Nicola.)
  482. Version 4.44 (25 October 2005):
  483. - Implemented a FastGetHeapStatus function in analogy with GetHeapStatus.
  484. (Suggested by Cristian Nicola.)
  485. - Shifted more of the stack trace code over to the support dll to allow third
  486. party vendors to make available their own stack tracing and stack trace
  487. logging facilities.
  488. - Mathias Rauen (madshi) improved the support for madExcept in the debug info
  489. support DLL. Thanks!
  490. - Added support for BCB5. (Thanks to Roddy Pratt.)
  491. - Added the Czech translation by Rene Mihula.
  492. - Added the "DetectMMOperationsAfterUninstall" option. This will catch
  493. attempts to use the MM after FastMM has been uninstalled, and is useful for
  494. debugging.
  495. Version 4.46 (26 October 2005):
  496. - Renamed FastMM_DebugInfo.dll to FastMM_FullDebugMode.dll and made the
  497. dependency on this library a static one. This solves a DLL unload order
  498. problem when using FullDebugMode together with the replacement
  499. borlndmm.dll. (Thanks to Bart van der Werf.)
  500. - Added the Polish translation by Artur Redzko.
  501. Version 4.48 (10 November 2005):
  502. - Fixed class detection for objects leaked in dynamically loaded DLLs that
  503. were relocated.
  504. - Fabio Dell'Aria implemented support for EurekaLog in the FullDebugMode
  505. support DLL. Thanks!
  506. - Added the Spanish translation by JRG ("The Delphi Guy").
  507. Version 4.49 (10 November 2005):
  508. - Implemented support for installing replacement AllocMem and leak
  509. registration mechanisms for Delphi/BCB versions that support it.
  510. - Added support for Delphi 4. (Thanks to Justus Janssen.)
  511. Version 4.50 (5 December 2005):
  512. - Renamed the ReportMemoryLeaks global variable to ReportMemoryLeaksOnShutdown
  513. to be more consistent with the Delphi 2006 memory manager.
  514. - Improved the handling of large blocks. Large blocks can now consist of
  515. several consecutive segments allocated through VirtualAlloc. This
  516. significantly improves speed when frequently resizing large blocks, since
  517. these blocks can now often be upsized in-place.
  518. Version 4.52 (7 December 2005):
  519. - Fixed the compilation error with Delphi 5. (Thanks to Vadim Lopushansky and
  520. Charles Vinal for reporting the error.)
  521. Version 4.54 (15 December 2005):
  522. - Added the Brazilian Portuguese translation by Johni Jeferson Capeletto.
  523. - Fixed the compilation error with BCB6. (Thanks to Kurt Fitzner.)
  524. Version 4.56 (20 December 2005):
  525. - Fixed the Kylix compilation problem. (Thanks to Michal Niklas.)
  526. Version 4.58 (1 February 2006):
  527. - Added the German translations by Thomas Speck and Uwe Queisser.
  528. - Added the Indonesian translation by Zaenal Mutaqin.
  529. - Added the Portuguese translation by Carlos Macao.
  530. Version 4.60 (21 February 2006):
  531. - Fixed a performance issue due to an unnecessary block move operation when
  532. allocating a block in the range 1261-1372 bytes and then reallocating it in
  533. the range 1373-1429 bytes twice. (Thanks to Michael Winter.)
  534. - Added the Belarussian translation by dzmitry[li].
  535. - Added the updated Spanish translation by Marcelo Montenegro.
  536. - Added a new option "EnableSharingWithDefaultMM". This option allows FastMM
  537. to be shared with the default MM of Delphi 2006. It is on by default, but
  538. MM sharing has to be enabled otherwise it has no effect (refer to the
  539. documentation for the "ShareMM" and "AttemptToUseSharedMM" options).
  540. Version 4.62 (22 February 2006):
  541. - Fixed a possible read access violation in the MoveX16LP routine when the
  542. UseCustomVariableSizeMoveRoutines option is enabled. (Thanks to Jud Cole for
  543. some great detective work in finding this bug.)
  544. - Improved the downsizing behaviour of medium blocks to better correlate with
  545. the reallocation behaviour of small blocks. This change reduces the number
  546. of transitions between small and medium block types when reallocating blocks
  547. in the 0.7K to 2.6K range. It cuts down on the number of memory move
  548. operations and improves performance.
  549. Version 4.64 (31 March 2006):
  550. - Added the following functions for use with FullDebugMode (and added the
  551. exports to the replacement BorlndMM.dll): SetMMLogFileName,
  552. GetCurrentAllocationGroup, PushAllocationGroup, PopAllocationGroup and
  553. LogAllocatedBlocksToFile. The purpose of these functions are to allow you to
  554. identify and log related memory leaks while your application is still
  555. running.
  556. - Fixed a bug in the memory manager sharing mechanism affecting Windows
  557. 95/98/ME. (Thanks to Zdenek Vasku.)
  558. Version 4.66 (9 May 2006):
  559. - Added a hint comment in this file so that FastMM4Messages.pas will also be
  560. backed up by GExperts. (Thanks to RB Winston.)
  561. - Fixed a bug affecting large address space (> 2GB) support under
  562. FullDebugMode. (Thanks to Thomas Schulz.)
  563. Version 4.68 (3 July 2006):
  564. - Added the Italian translation by Luigi Sandon.
  565. - If FastMM is used inside a DLL it will now use the name of the DLL as base
  566. for the log file name. (Previously it always used the name of the main
  567. application executable file.)
  568. - Fixed a rare A/V when both the FullDebugMode and RawStackTraces options were
  569. enabled. (Thanks to Primoz Gabrijelcic.)
  570. - Added the "NeverSleepOnThreadContention" option. This option may improve
  571. performance if the ratio of the the number of active threads to the number
  572. of CPU cores is low (typically < 2). This option is only useful for 4+ CPU
  573. systems, it almost always hurts performance on single and dual CPU systems.
  574. (Thanks to Werner Bochtler and Markus Beth.)
  575. Version 4.70 (4 August 2006):
  576. - Added the Simplified Chinese translation by JiYuan Xie.
  577. - Added the updated Russian as well as the Ukrainian translation by Andrey
  578. Shtukaturov.
  579. - Fixed two bugs in the leak class detection code that would sometimes fail
  580. to detect the class of leaked objects and strings, and report them as
  581. 'unknown'. (Thanks to Dimitry Timokhov)
  582. Version 4.72 (24 September 2006):
  583. - Fixed a bug that caused AllocMem to not clear blocks > 256K in
  584. FullDebugMode. (Thanks to Paulo Moreno.)
  585. Version 4.74 (9 November 2006):
  586. - Fixed a bug in the segmented large block functionality that could lead to
  587. an application freeze when upsizing blocks greater than 256K in a
  588. multithreaded application (one of those "what the heck was I thinking?"
  589. type bugs).
  590. Version 4.76 (12 January 2007):
  591. - Changed the RawStackTraces code in the FullDebugMode DLL
  592. to prevent it from modifying the Windows "GetLastError" error code.
  593. (Thanks to Primoz Gabrijelcic.)
  594. - Fixed a threading issue when the "CheckHeapForCorruption" option was
  595. enabled, but the "FullDebugMode" option was disabled. (Thanks to Primoz
  596. Gabrijelcic.)
  597. - Removed some unnecessary startup code when the MM sharing mechanism is
  598. disabled. (Thanks to Vladimir Bochkarev.)
  599. - In FullDebugMode leaked blocks would sometimes be reported as belonging to
  600. the class "TFreedObject" if they were allocated but never used. Such blocks
  601. will now be reported as "unknown". (Thanks to Francois Malan.)
  602. - In recent versions the replacement borlndmm.dll created a log file (when
  603. enabled) that used the "borlndmm" prefix instead of the application name.
  604. It is now fixed to use the application name, however if FastMM is used
  605. inside other DLLs the name of those DLLs will be used. (Thanks to Bart van
  606. der Werf.)
  607. - Added a "FastMMVersion" constant. (Suggested by Loris Luise.)
  608. - Fixed an issue with error message boxes not displaying under certain
  609. configurations. (Thanks to J.W. de Bokx.)
  610. - FastMM will now display only one error message at a time. If many errors
  611. occur in quick succession, only the first error will be shown (but all will
  612. be logged). This avoids a stack overflow with badly misbehaved programs.
  613. (Thanks to Bart van der Werf.)
  614. - Added a LoadDebugDLLDynamically option to be used in conjunction with
  615. FullDebugMode. In this mode FastMM_FullDebugMode.dll is loaded dynamically.
  616. If the DLL cannot be found, stack traces will not be available. (Thanks to
  617. Rene Mihula.)
  618. Version 4.78 (1 March 2007):
  619. - The MB_DEFAULT_DESKTOP_ONLY constant that is used when displaying messages
  620. boxes since 4.76 is not defined under Kylix, and the source would thus not
  621. compile. That constant is now defined. (Thanks to Werner Bochtler.)
  622. - Moved the medium block locking code that was duplicated in several places
  623. to a subroutine to reduce code size. (Thanks to Hallvard Vassbotn.)
  624. - Fixed a bug in the leak registration code that sometimes caused registered
  625. leaks to be reported erroneously. (Thanks to Primoz Gabrijelcic.)
  626. - Added the NoDebugInfo option (on by default) that suppresses the generation
  627. of debug info for the FastMM4.pas unit. This will prevent the integrated
  628. debugger from stepping into the memory manager. (Thanks to Primoz
  629. Gabrijelcic.)
  630. - Increased the default stack trace depth in FullDebugMode from 9 to 10 to
  631. ensure that the Align16Bytes setting works in FullDebugMode. (Thanks to
  632. Igor Lindunen.)
  633. - Updated the Czech translation. (Thanks to Rene Mihula.)
  634. Version 4.84 (7 July 2008):
  635. - Added the Romanian translation. (Thanks to Ionut Muntean.)
  636. - Optimized the GetMemoryMap procedure to improve speed.
  637. - Added the GetMemoryManagerUsageSummary function that returns a summary of
  638. the GetMemoryManagerState call. (Thanks to Hallvard Vassbotn.)
  639. - Added the French translation. (Thanks to Florent Ouchet.)
  640. - Added the "AlwaysAllocateTopDown" FullDebugMode option to help with
  641. catching bad pointer arithmetic code in an address space > 2GB. This option
  642. is enabled by default.
  643. - Added the "InstallOnlyIfRunningInIDE" option. Enable this option to
  644. only install FastMM as the memory manager when the application is run
  645. inside the Delphi IDE. This is useful when you want to deploy the same EXE
  646. that you use for testing, but only want the debugging features active on
  647. development machines. When this option is enabled and the application is
  648. not being run inside the IDE, then the default Delphi memory manager will
  649. be used (which, since Delphi 2006, is FastMM without FullDebugMode.) This
  650. option is off by default.
  651. - Added the "FullDebugModeInIDE" option. This is a convenient shorthand for
  652. enabling FullDebugMode, InstallOnlyIfRunningInIDE and
  653. LoadDebugDLLDynamically. This causes FastMM to be used in FullDebugMode
  654. when the application is being debugged on development machines, and the
  655. default memory manager when the same executable is deployed. This allows
  656. the debugging and deployment of an application without having to compile
  657. separate executables. This option is off by default.
  658. - Added a ScanMemoryPoolForCorruptions procedure that checks the entire
  659. memory pool for corruptions and raises an exception if one is found. It can
  660. be called at any time, but is only available in FullDebugMode. (Thanks to
  661. Marcus Mönnig.)
  662. - Added a global variable "FullDebugModeScanMemoryPoolBeforeEveryOperation".
  663. When this variable is set to true and FullDebugMode is enabled, then the
  664. entire memory pool is checked for consistency before every GetMem, FreeMem
  665. and ReallocMem operation. An "Out of Memory" error is raised if a
  666. corruption is found (and this variable is set to false to prevent recursive
  667. errors). This obviously incurs a massive performance hit, so enable it only
  668. when hunting for elusive memory corruption bugs. (Thanks to Marcus Mönnig.)
  669. - Fixed a bug in AllocMem that caused the FPU stack to be shifted by one
  670. position.
  671. - Changed the default for option "EnableMMX" to false, since using MMX may
  672. cause unexpected behaviour in code that passes parameters on the FPU stack
  673. (like some "compiler magic" routines, e.g. VarFromReal).
  674. - Removed the "EnableSharingWithDefaultMM" option. This is now the default
  675. behaviour and cannot be disabled. (FastMM will always try to share memory
  676. managers between itself and the default memory manager when memory manager
  677. sharing is enabled.)
  678. - Introduced a new memory manager sharing mechanism based on memory mapped
  679. files. This solves compatibility issues with console and service
  680. applications. This sharing mechanism currently runs in parallel with the
  681. old mechanism, but the old mechanism can be disabled by undefining
  682. "EnableBackwardCompatibleMMSharing" in FastMM4Options.inc.
  683. - Fixed the recursive call error when the EnableMemoryLeakReporting option
  684. is disabled and an attempt is made to register a memory leak under Delphi
  685. 2006 or later. (Thanks to Thomas Schulz.)
  686. - Added a global variable "SuppressMessageBoxes" to enable or disable
  687. messageboxes at runtime. (Thanks to Craig Peterson.)
  688. - Added the leak reporting code for C++ Builder, as well as various other
  689. C++ Builder bits written by JiYuan Xie. (Thank you!)
  690. - Added the new Usage Tracker written by Hanspeter Widmer. (Thank you!)
  691. Version 4.86 (31 July 2008):
  692. - Tweaked the string detection algorithm somewhat to be less strict, and
  693. allow non-class leaks to be more often categorized as strings.
  694. - Fixed a compilation error under Delphi 5.
  695. - Made LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions thread
  696. safe. (Thanks to Francois Piette.)
  697. Version 4.88 (13 August 2008):
  698. - Fixed compiler warnings in NoOpRegisterExpectedMemoryLeak and
  699. NoOpUnRegisterExpectedMemoryLeak. (Thanks to Michael Rabatscher.)
  700. - Added the Simplified Chinese translation of FastMM4Options.inc by
  701. QianYuan Wang. (Thank you!)
  702. - Included the updated C++ Builder files with support for BCB6 without
  703. update 4 applied. (Submitted by JiYuan Xie. Thanks!)
  704. - Fixed a compilation error under Delphi 5.
  705. - Made LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions thread
  706. safe - for real this time. (Thanks to Francois Piette.)
  707. Version 4.90 (9 September 2008):
  708. - Added logging of the thread ID when capturing and displaying stack
  709. traces. (Suggested by Allen Bauer and Mark Edington.)
  710. - Fixed a Delphi 5 compiler error under FullDebugMode. (Thanks to Maurizio
  711. Lotauro and Christian-W. Budde.)
  712. - Changed a default setting in FastMM4Options.inc: RawStackTraces is now
  713. off by default due to the high number of support requests I receive with
  714. regards to the false postives it may cause. I recommend compiling debug
  715. builds of applications with the "Stack Frames" option enabled.
  716. - Fixed a compilation error under Kylix. (Thanks to Werner Bochtler.)
  717. - Official support for Delphi 2009.
  718. Version 4.92 (25 November 2008):
  719. - Added the DisableLoggingOfMemoryDumps option under FullDebugMode. When
  720. this option is set, memory dumps will not be logged for memory leaks or
  721. errors. (Thanks to Patrick van Logchem.)
  722. - Exposed the class and string type detection code in the interface section
  723. for use in application code (if required). (Requested by Patrick van
  724. Logchem.)
  725. - Fixed a bug in SetMMLogFileName that could cause the log file name to be
  726. set incorrectly.
  727. - Added BCB4 support. (Thanks to Norbert Spiegel.)
  728. - Included the updated Czech translation by Rene Mihula.
  729. - When FastMM raises an error due to a freed block being modified, it now
  730. logs detail about which bytes in the block were modified.
  731. Version 4.94 (28 August 2009):
  732. - Added the DoNotInstallIfDLLMissing option that prevents FastMM from
  733. installing itself if the FastMM_FullDebugMode.dll library is not
  734. available. (Only applicable when FullDebugMode and LoadDebugDLLDynamically
  735. are both enabled.) This is useful when the same executable will be used for
  736. both debugging and deployment - when the debug support DLL is available
  737. FastMM will be installed in FullDebugMode, and otherwise the default memory
  738. manager will be used.
  739. - Added the FullDebugModeWhenDLLAvailable option that combines the
  740. FullDebugMode, LoadDebugDLLDynamically and DoNotInstallIfDLLMissing options.
  741. - Re-enabled RawStackTraces by default. The frame based stack traces (even
  742. when compiling with stack frames enabled) are generally too incomplete.
  743. - Improved the speed of large block operations under FullDebugMode: Since
  744. large blocks are never reused, there is no point in clearing them before
  745. and after use (so it does not do that anymore).
  746. - If an error occurs in FullDebugMode and FastMM is unable to append to the
  747. log file, it will attempt to write to a log file of the same name in the
  748. "My Documents" folder. This feature is helpful when the executable resides
  749. in a read-only location and the default log file, which is derived from the
  750. executable name, would thus not be writeable.
  751. - Added support for controlling the error log file location through an
  752. environment variable. If the 'FastMMLogFilePath' environment variable is
  753. set then any generated error logs will be written to the specified folder
  754. instead of the default location (which is the same folder as the
  755. application).
  756. - Improved the call instruction detection code in the FastMM_FullDebugMode
  757. library. (Thanks to the JCL team.)
  758. - Improved the string leak detection and reporting code. (Thanks to Uwe
  759. Schuster.)
  760. - New FullDebugMode feature: Whenever FreeMem or ReallocMem is called, FastMM
  761. will check that the block was actually allocated through the same FastMM
  762. instance. This is useful for tracking down memory manager sharing issues.
  763. - Compatible with Delphi 2010.
  764. Version 4.96 (31 August 2010):
  765. - Reduced the minimum block size to 4 bytes from the previous value of 12
  766. bytes (only applicable to 8 byte alignment). This reduces memory usage if
  767. the application allocates many blocks <= 4 bytes in size.
  768. - Added colour-coded change indication to the FastMM usage tracker, making
  769. it easier to spot changes in the memory usage grid. (Thanks to Murray
  770. McGowan.)
  771. - Added the SuppressFreeMemErrorsInsideException FullDebugMode option: If
  772. FastMM encounters a problem with a memory block inside the FullDebugMode
  773. FreeMem handler then an "invalid pointer operation" exception will usually
  774. be raised. If the FreeMem occurs while another exception is being handled
  775. (perhaps in the try.. finally code) then the original exception will be
  776. lost. With this option set FastMM will ignore errors inside FreeMem when an
  777. exception is being handled, thus allowing the original exception to
  778. propagate. This option is on by default. (Thanks to Michael Hieke.)
  779. - Fixed Windows 95 FullDebugMode support that was broken in 4.94. (Thanks to
  780. Richard Bradbrook.)
  781. - Fixed a bug affecting GetMemoryMap performance and accuracy of measurements
  782. above 2GB if a large address space is not enabled for the project. (Thanks
  783. to Michael Hieke.)
  784. - Added the FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak boolean flag.
  785. When set, all allocations are automatically registered as expected memory
  786. leaks. Only available in FullDebugMode. (Thanks to Brian Cook.)
  787. - Compatible with Delphi XE.
  788. Version 4.97 (30 September 2010):
  789. - Fixed a crash bug (that crept in in 4.96) that may manifest itself when
  790. resizing a block to 4 bytes or less.
  791. - Added the UseSwitchToThread option. Set this option to call SwitchToThread
  792. instead of sitting in a "busy waiting" loop when a thread contention
  793. occurs. This is used in conjunction with the NeverSleepOnThreadContention
  794. option, and has no effect unless NeverSleepOnThreadContention is also
  795. defined. This option may improve performance with many CPU cores and/or
  796. threads of different priorities. Note that the SwitchToThread API call is
  797. only available on Windows 2000 and later. (Thanks to Zach Saw.)
  798. Version 4.98 (23 September 2011)
  799. - Added the FullDebugModeCallBacks define which adds support for memory
  800. manager event callbacks. This allows the application to be notified of
  801. memory allocations, frees and reallocations as they occur. (Thanks to
  802. Jeroen Pluimers.)
  803. - Added security options ClearMemoryBeforeReturningToOS and
  804. AlwaysClearFreedMemory to force the clearing of memory blocks after being
  805. freed. This could possibly provide some protection against information
  806. theft, but at a significant performance penalty. (Thanks to Andrey
  807. Sozonov.)
  808. - Shifted the code in the initialization section to a procedure
  809. RunInitializationCode. This allows the startup code to be called before
  810. InitUnits, which is required by some software protection tools.
  811. - Added support for Delphi XE2 (Windows 32-bit and Windows 64-bit platforms
  812. only).
  813. *)
  814. unit FastMM4;
  815. interface
  816. {$Include FastMM4Options.inc}
  817. {$RANGECHECKS OFF}
  818. {$BOOLEVAL OFF}
  819. {$OVERFLOWCHECKS OFF}
  820. {$OPTIMIZATION ON}
  821. {$TYPEDADDRESS OFF}
  822. {$LONGSTRINGS ON}
  823. {Compiler version defines}
  824. {$ifndef BCB}
  825. {$ifdef ver120}
  826. {$define Delphi4or5}
  827. {$endif}
  828. {$ifdef ver130}
  829. {$define Delphi4or5}
  830. {$endif}
  831. {$ifdef ver140}
  832. {$define Delphi6}
  833. {$endif}
  834. {$ifdef ver150}
  835. {$define Delphi7}
  836. {$endif}
  837. {$ifdef ver170}
  838. {$define Delphi2005}
  839. {$endif}
  840. {$else}
  841. {for BCB4, use the Delphi 5 codepath}
  842. {$ifdef ver120}
  843. {$define Delphi4or5}
  844. {$define BCB4}
  845. {$endif}
  846. {for BCB5, use the Delphi 5 codepath}
  847. {$ifdef ver130}
  848. {$define Delphi4or5}
  849. {$endif}
  850. {$endif}
  851. {$ifdef ver180}
  852. {$define BDS2006}
  853. {$endif}
  854. {$define 32Bit}
  855. {$ifndef Delphi4or5}
  856. {$if SizeOf(Pointer) = 8}
  857. {$define 64Bit}
  858. {$undef 32Bit}
  859. {$ifend}
  860. {$if CompilerVersion >= 23}
  861. {$define XE2AndUp}
  862. {$ifend}
  863. {$define BCB6OrDelphi6AndUp}
  864. {$ifndef BCB}
  865. {$define Delphi6AndUp}
  866. {$endif}
  867. {$ifndef Delphi6}
  868. {$define BCB6OrDelphi7AndUp}
  869. {$ifndef BCB}
  870. {$define Delphi7AndUp}
  871. {$endif}
  872. {$ifndef BCB}
  873. {$ifndef Delphi7}
  874. {$ifndef Delphi2005}
  875. {$define BDS2006AndUp}
  876. {$endif}
  877. {$endif}
  878. {$endif}
  879. {$endif}
  880. {$endif}
  881. {$ifdef 64Bit}
  882. {Under 64 bit memory blocks must always be 16-byte aligned}
  883. {$define Align16Bytes}
  884. {No need for MMX under 64-bit, since SSE2 is available}
  885. {$undef EnableMMX}
  886. {There is little need for raw stack traces under 64-bit, since frame based
  887. stack traces are much more accurate than under 32-bit. (And frame based
  888. stack tracing is much faster.)}
  889. {$undef RawStackTraces}
  890. {$endif}
  891. {IDE debug mode always enables FullDebugMode and dynamic loading of the FullDebugMode DLL.}
  892. {$ifdef FullDebugModeInIDE}
  893. {$define InstallOnlyIfRunningInIDE}
  894. {$define FullDebugMode}
  895. {$define LoadDebugDLLDynamically}
  896. {$endif}
  897. {Install in FullDebugMode only when the DLL is available?}
  898. {$ifdef FullDebugModeWhenDLLAvailable}
  899. {$define FullDebugMode}
  900. {$define LoadDebugDLLDynamically}
  901. {$define DoNotInstallIfDLLMissing}
  902. {$endif}
  903. {Some features not currently supported under Kylix}
  904. {$ifdef Linux}
  905. {$undef FullDebugMode}
  906. {$undef LogErrorsToFile}
  907. {$undef LogMemoryLeakDetailToFile}
  908. {$undef ShareMM}
  909. {$undef AttemptToUseSharedMM}
  910. {$undef RequireIDEPresenceForLeakReporting}
  911. {$undef UseOutputDebugString}
  912. {$ifdef PIC}
  913. {BASM version does not support position independent code}
  914. {$undef ASMVersion}
  915. {$endif}
  916. {$endif}
  917. {Do we require debug info for leak checking?}
  918. {$ifdef RequireDebugInfoForLeakReporting}
  919. {$ifopt D-}
  920. {$undef EnableMemoryLeakReporting}
  921. {$endif}
  922. {$endif}
  923. {Enable heap checking and leak reporting in full debug mode}
  924. {$ifdef FullDebugMode}
  925. {$STACKFRAMES ON}
  926. {$define CheckHeapForCorruption}
  927. {$ifndef CatchUseOfFreedInterfaces}
  928. {$define CheckUseOfFreedBlocksOnShutdown}
  929. {$endif}
  930. {$else}
  931. {Error logging requires FullDebugMode}
  932. {$undef LogErrorsToFile}
  933. {$undef CatchUseOfFreedInterfaces}
  934. {$undef RawStackTraces}
  935. {$undef AlwaysAllocateTopDown}
  936. {$endif}
  937. {Set defines for security options}
  938. {$ifdef FullDebugMode}
  939. {In FullDebugMode small and medium blocks are always cleared when calling
  940. FreeMem. Large blocks are always returned to the OS immediately.}
  941. {$ifdef ClearMemoryBeforeReturningToOS}
  942. {$define ClearLargeBlocksBeforeReturningToOS}
  943. {$endif}
  944. {$ifdef AlwaysClearFreedMemory}
  945. {$define ClearLargeBlocksBeforeReturningToOS}
  946. {$endif}
  947. {$else}
  948. {If memory blocks are cleared in FreeMem then they do not need to be cleared
  949. before returning the memory to the OS.}
  950. {$ifdef AlwaysClearFreedMemory}
  951. {$define ClearSmallAndMediumBlocksInFreeMem}
  952. {$define ClearLargeBlocksBeforeReturningToOS}
  953. {$else}
  954. {$ifdef ClearMemoryBeforeReturningToOS}
  955. {$define ClearMediumBlockPoolsBeforeReturningToOS}
  956. {$define ClearLargeBlocksBeforeReturningToOS}
  957. {$endif}
  958. {$endif}
  959. {$endif}
  960. {Only the Pascal version supports extended heap corruption checking.}
  961. {$ifdef CheckHeapForCorruption}
  962. {$undef ASMVersion}
  963. {$endif}
  964. {For BASM bits that are not implemented in 64-bit.}
  965. {$ifdef 32Bit}
  966. {$ifdef ASMVersion}
  967. {$define Use32BitAsm}
  968. {$endif}
  969. {$endif}
  970. {$ifdef UseRuntimePackages}
  971. {$define AssumeMultiThreaded}
  972. {$endif}
  973. {$ifdef BCB6OrDelphi6AndUp}
  974. {$WARN SYMBOL_PLATFORM OFF}
  975. {$WARN SYMBOL_DEPRECATED OFF}
  976. {$endif}
  977. {Leak detail logging requires error logging}
  978. {$ifndef LogErrorsToFile}
  979. {$undef LogMemoryLeakDetailToFile}
  980. {$undef ClearLogFileOnStartup}
  981. {$endif}
  982. {$ifndef EnableMemoryLeakReporting}
  983. {Manual leak reporting control requires leak reporting to be enabled}
  984. {$undef ManualLeakReportingControl}
  985. {$endif}
  986. {$ifndef EnableMMX}
  987. {$undef ForceMMX}
  988. {$endif}
  989. {Are any of the MM sharing options enabled?}
  990. {$ifdef ShareMM}
  991. {$define MMSharingEnabled}
  992. {$endif}
  993. {$ifdef AttemptToUseSharedMM}
  994. {$define MMSharingEnabled}
  995. {$endif}
  996. {Instruct GExperts to back up the messages file as well.}
  997. {#BACKUP FastMM4Messages.pas}
  998. {Should debug info be disabled?}
  999. {$ifdef NoDebugInfo}
  1000. {$DEBUGINFO OFF}
  1001. {$endif}
  1002. {$ifdef BCB}
  1003. {$ifdef borlndmmdll}
  1004. {$OBJEXPORTALL OFF}
  1005. {$endif}
  1006. {$ifndef PatchBCBTerminate}
  1007. {Cannot uninstall safely under BCB}
  1008. {$define NeverUninstall}
  1009. {Disable memory leak reporting}
  1010. {$undef EnableMemoryLeakReporting}
  1011. {$endif}
  1012. {$endif}
  1013. {-------------------------Public constants-----------------------------}
  1014. const
  1015. {The current version of FastMM}
  1016. FastMMVersion = '4.98';
  1017. {The number of small block types}
  1018. {$ifdef Align16Bytes}
  1019. NumSmallBlockTypes = 46;
  1020. {$else}
  1021. NumSmallBlockTypes = 56;
  1022. {$endif}
  1023. {----------------------------Public types------------------------------}
  1024. type
  1025. {Make sure all the required types are available}
  1026. {$ifdef BCB6OrDelphi6AndUp}
  1027. {$if CompilerVersion < 20}
  1028. PByte = PAnsiChar;
  1029. {$ifend}
  1030. {$if CompilerVersion < 23}
  1031. NativeInt = Integer;
  1032. NativeUInt = Cardinal;
  1033. PNativeUInt = ^Cardinal;
  1034. IntPtr = Integer;
  1035. UIntPtr = Cardinal;
  1036. {$ifend}
  1037. {$else}
  1038. PByte = PAnsiChar;
  1039. NativeInt = Integer;
  1040. NativeUInt = Cardinal;
  1041. PNativeUInt = ^Cardinal;
  1042. IntPtr = Integer;
  1043. UIntPtr = Cardinal;
  1044. {$endif}
  1045. TSmallBlockTypeState = packed record
  1046. {The internal size of the block type}
  1047. InternalBlockSize: Cardinal;
  1048. {Useable block size: The number of non-reserved bytes inside the block.}
  1049. UseableBlockSize: Cardinal;
  1050. {The number of allocated blocks}
  1051. AllocatedBlockCount: NativeUInt;
  1052. {The total address space reserved for this block type (both allocated and
  1053. free blocks)}
  1054. ReservedAddressSpace: NativeUInt;
  1055. end;
  1056. TSmallBlockTypeStates = array[0..NumSmallBlockTypes - 1] of TSmallBlockTypeState;
  1057. TMemoryManagerState = packed record
  1058. {Small block type states}
  1059. SmallBlockTypeStates: TSmallBlockTypeStates;
  1060. {Medium block stats}
  1061. AllocatedMediumBlockCount: Cardinal;
  1062. TotalAllocatedMediumBlockSize: NativeUInt;
  1063. ReservedMediumBlockAddressSpace: NativeUInt;
  1064. {Large block stats}
  1065. AllocatedLargeBlockCount: Cardinal;
  1066. TotalAllocatedLargeBlockSize: NativeUInt;
  1067. ReservedLargeBlockAddressSpace: NativeUInt;
  1068. end;
  1069. TMemoryManagerUsageSummary = packed record
  1070. {The total number of bytes allocated by the application.}
  1071. AllocatedBytes: NativeUInt;
  1072. {The total number of address space bytes used by control structures, or
  1073. lost due to fragmentation and other overhead.}
  1074. OverheadBytes: NativeUInt;
  1075. {The efficiency of the memory manager expressed as a percentage. This is
  1076. 100 * AllocatedBytes / (AllocatedBytes + OverheadBytes).}
  1077. EfficiencyPercentage: Double;
  1078. end;
  1079. {Memory map}
  1080. TChunkStatus = (csUnallocated, csAllocated, csReserved, csSysAllocated,
  1081. csSysReserved);
  1082. TMemoryMap = array[0..65535] of TChunkStatus;
  1083. {$ifdef EnableMemoryLeakReporting}
  1084. {List of registered leaks}
  1085. TRegisteredMemoryLeak = packed record
  1086. LeakAddress: Pointer;
  1087. LeakedClass: TClass;
  1088. {$ifdef CheckCppObjectTypeEnabled}
  1089. LeakedCppTypeIdPtr: Pointer;
  1090. {$endif}
  1091. LeakSize: NativeInt;
  1092. LeakCount: Integer;
  1093. end;
  1094. TRegisteredMemoryLeaks = array of TRegisteredMemoryLeak;
  1095. {$endif}
  1096. {Used by the DetectStringData routine to detect whether a leaked block
  1097. contains string data.}
  1098. TStringDataType = (stUnknown, stAnsiString, stUnicodeString);
  1099. {--------------------------Public variables----------------------------}
  1100. var
  1101. {If this variable is set to true and FullDebugMode is enabled, then the
  1102. entire memory pool is checked for consistency before every memory
  1103. operation. Note that this incurs a massive performance hit on top of
  1104. the already significant FullDebugMode overhead, so enable this option
  1105. only when absolutely necessary.}
  1106. FullDebugModeScanMemoryPoolBeforeEveryOperation: Boolean = False;
  1107. FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak: Boolean = False;
  1108. {$ifdef ManualLeakReportingControl}
  1109. {Variable is declared in system.pas in newer Delphi versions.}
  1110. {$ifndef BDS2006AndUp}
  1111. ReportMemoryLeaksOnShutdown: Boolean;
  1112. {$endif}
  1113. {$endif}
  1114. {If set to True, disables the display of all messageboxes}
  1115. SuppressMessageBoxes: Boolean;
  1116. {-------------------------Public procedures----------------------------}
  1117. {Executes the code normally run in the initialization section. Running it
  1118. earlier may be required with e.g. some software protection tools.}
  1119. procedure RunInitializationCode;
  1120. {Installation procedures must be exposed for the BCB helper unit FastMM4BCB.cpp}
  1121. {$ifdef BCB}
  1122. procedure InitializeMemoryManager;
  1123. function CheckCanInstallMemoryManager: Boolean;
  1124. procedure InstallMemoryManager;
  1125. {$ifdef FullDebugMode}
  1126. (*$HPPEMIT '#define FullDebugMode' *)
  1127. {$ifdef ClearLogFileOnStartup}
  1128. (*$HPPEMIT ' #define ClearLogFileOnStartup' *)
  1129. procedure DeleteEventLog;
  1130. {$endif}
  1131. {$ifdef LoadDebugDLLDynamically}
  1132. (*$HPPEMIT ' #define LoadDebugDLLDynamically' *)
  1133. {$endif}
  1134. {$ifdef RawStackTraces}
  1135. (*$HPPEMIT ' #define RawStackTraces' *)
  1136. {$endif}
  1137. {$endif}
  1138. {$ifdef PatchBCBTerminate}
  1139. (*$HPPEMIT ''#13#10 *)
  1140. (*$HPPEMIT '#define PatchBCBTerminate' *)
  1141. {$ifdef EnableMemoryLeakReporting}
  1142. (*$HPPEMIT ''#13#10 *)
  1143. (*$HPPEMIT '#define EnableMemoryLeakReporting' *)
  1144. {$endif}
  1145. {$ifdef DetectMMOperationsAfterUninstall}
  1146. (*$HPPEMIT ''#13#10 *)
  1147. (*$HPPEMIT '#define DetectMMOperationsAfterUninstall' *)
  1148. {$endif}
  1149. {Called in FastMM4BCB.cpp, should contain codes of original "finalization" section}
  1150. procedure FinalizeMemoryManager;
  1151. {For completion of "RequireDebuggerPresenceForLeakReporting" checking in "FinalizeMemoryManager"}
  1152. var
  1153. pCppDebugHook: ^Integer = nil; //PInteger not defined in BCB5
  1154. {$ifdef CheckCppObjectTypeEnabled}
  1155. (*$HPPEMIT ''#13#10 *)
  1156. (*$HPPEMIT '#define CheckCppObjectTypeEnabled' *)
  1157. type
  1158. TGetCppVirtObjSizeByTypeIdPtrFunc = function(APointer: Pointer): Cardinal;
  1159. TGetCppVirtObjTypeIdPtrFunc = function(APointer: Pointer; ASize: Cardinal): Pointer;
  1160. TGetCppVirtObjTypeNameFunc = function(APointer: Pointer; ASize: Cardinal): PAnsiChar;
  1161. TGetCppVirtObjTypeNameByTypeIdPtrFunc = function (APointer: Pointer): PAnsiChar;
  1162. TGetCppVirtObjTypeNameByVTablePtrFunc = function(AVTablePtr: Pointer; AVTablePtrOffset: Cardinal): PAnsiChar;
  1163. var
  1164. {Return virtual object's size from typeId pointer}
  1165. GetCppVirtObjSizeByTypeIdPtrFunc: TGetCppVirtObjSizeByTypeIdPtrFunc = nil;
  1166. {Retrieve virtual object's typeId pointer}
  1167. GetCppVirtObjTypeIdPtrFunc: TGetCppVirtObjTypeIdPtrFunc = nil;
  1168. {Retrieve virtual object's type name}
  1169. GetCppVirtObjTypeNameFunc: TGetCppVirtObjTypeNameFunc = nil;
  1170. {Return virtual object's type name from typeId pointer}
  1171. GetCppVirtObjTypeNameByTypeIdPtrFunc: TGetCppVirtObjTypeNameByTypeIdPtrFunc = nil;
  1172. {Retrieve virtual object's typeId pointer from it's virtual table pointer}
  1173. GetCppVirtObjTypeNameByVTablePtrFunc: TGetCppVirtObjTypeNameByVTablePtrFunc = nil;
  1174. {$endif}
  1175. {$endif}
  1176. {$endif}
  1177. {$ifndef FullDebugMode}
  1178. {The standard memory manager functions}
  1179. function FastGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  1180. function FastFreeMem(APointer: Pointer): Integer;
  1181. function FastReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  1182. function FastAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  1183. {$else}
  1184. {The FullDebugMode memory manager functions}
  1185. function DebugGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  1186. function DebugFreeMem(APointer: Pointer): Integer;
  1187. function DebugReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  1188. function DebugAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  1189. {Scans the memory pool for any corruptions. If a corruption is encountered an "Out of Memory" exception is
  1190. raised.}
  1191. procedure ScanMemoryPoolForCorruptions;
  1192. {Specify the full path and name for the filename to be used for logging memory
  1193. errors, etc. If ALogFileName is nil or points to an empty string it will
  1194. revert to the default log file name.}
  1195. procedure SetMMLogFileName(ALogFileName: PAnsiChar = nil);
  1196. {Returns the current "allocation group". Whenever a GetMem request is serviced
  1197. in FullDebugMode, the current "allocation group" is stored in the block header.
  1198. This may help with debugging. Note that if a block is subsequently reallocated
  1199. that it keeps its original "allocation group" and "allocation number" (all
  1200. allocations are also numbered sequentially).}
  1201. function GetCurrentAllocationGroup: Cardinal;
  1202. {Allocation groups work in a stack like fashion. Group numbers are pushed onto
  1203. and popped off the stack. Note that the stack size is limited, so every push
  1204. should have a matching pop.}
  1205. procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
  1206. procedure PopAllocationGroup;
  1207. {Logs detail about currently allocated memory blocks for the specified range of
  1208. allocation groups. if ALastAllocationGroupToLog is less than
  1209. AFirstAllocationGroupToLog or it is zero, then all allocation groups are
  1210. logged. This routine also checks the memory pool for consistency at the same
  1211. time, raising an "Out of Memory" error if the check fails.}
  1212. procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
  1213. {$endif}
  1214. {Releases all allocated memory (use with extreme care)}
  1215. procedure FreeAllMemory;
  1216. {Returns summarised information about the state of the memory manager. (For
  1217. backward compatibility.)}
  1218. function FastGetHeapStatus: THeapStatus;
  1219. {Returns statistics about the current state of the memory manager}
  1220. procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
  1221. {Returns a summary of the information returned by GetMemoryManagerState}
  1222. procedure GetMemoryManagerUsageSummary(
  1223. var AMemoryManagerUsageSummary: TMemoryManagerUsageSummary);
  1224. {$ifndef Linux}
  1225. {Gets the state of every 64K block in the 4GB address space}
  1226. procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
  1227. {$endif}
  1228. {$ifdef EnableMemoryLeakReporting}
  1229. {Registers expected memory leaks. Returns true on success. The list of leaked
  1230. blocks is limited, so failure is possible if the list is full.}
  1231. function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
  1232. function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
  1233. function RegisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
  1234. {$ifdef CheckCppObjectTypeEnabled}
  1235. {Registers expected memory leaks by virtual object's typeId pointer.
  1236. Usage: RegisterExpectedMemoryLeak(typeid(ACppObject).tpp, Count);}
  1237. function RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): boolean; overload;
  1238. {$endif}
  1239. {Removes expected memory leaks. Returns true on success.}
  1240. function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
  1241. function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
  1242. function UnregisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
  1243. {$ifdef CheckCppObjectTypeEnabled}
  1244. {Usage: UnregisterExpectedMemoryLeak(typeid(ACppObject).tpp, Count);}
  1245. function UnregisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): boolean; overload;
  1246. {$endif}
  1247. {Returns a list of all expected memory leaks}
  1248. function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
  1249. {$endif}
  1250. {Returns the class for a memory block. Returns nil if it is not a valid class.
  1251. Used by the leak detection code.}
  1252. function DetectClassInstance(APointer: Pointer): TClass;
  1253. {Detects the probable string data type for a memory block. Used by the leak
  1254. classification code when a block cannot be identified as a known class
  1255. instance.}
  1256. function DetectStringData(APMemoryBlock: Pointer;
  1257. AAvailableSpaceInBlock: NativeInt): TStringDataType;
  1258. {$ifdef FullDebugMode}
  1259. {-------------FullDebugMode constants---------------}
  1260. const
  1261. {The stack trace depth. (Must be an *uneven* number to ensure that the
  1262. Align16Bytes option works in FullDebugMode.)}
  1263. StackTraceDepth = 11;
  1264. {The number of entries in the allocation group stack}
  1265. AllocationGroupStackSize = 1000;
  1266. {The number of fake VMT entries - used to track virtual method calls on
  1267. freed objects. Do not change this value without also updating TFreedObject.GetVirtualMethodIndex}
  1268. MaxFakeVMTEntries = 200;
  1269. {The pattern used to fill unused memory}
  1270. DebugFillByte = $80;
  1271. {$ifdef 32Bit}
  1272. DebugFillPattern = $01010101 * Cardinal(DebugFillByte);
  1273. {The address that is reserved so that accesses to the address of the fill
  1274. pattern will result in an A/V. (Not used under 64-bit, since the upper half
  1275. of the address space is always reserved by the OS.)}
  1276. DebugReservedAddress = $01010000 * Cardinal(DebugFillByte);
  1277. {$else}
  1278. DebugFillPattern = $8080808080808080;
  1279. {$endif}
  1280. {-------------------------FullDebugMode structures--------------------}
  1281. type
  1282. PStackTrace = ^TStackTrace;
  1283. TStackTrace = array[0..StackTraceDepth - 1] of NativeUInt;
  1284. TBlockOperation = (boBlockCheck, boGetMem, boFreeMem, boReallocMem);
  1285. {The header placed in front of blocks in FullDebugMode (just after the
  1286. standard header). Must be a multiple of 16 bytes in size otherwise the
  1287. Align16Bytes option will not work. Current size = 128 bytes under 32-bit,
  1288. and 240 bytes under 64-bit.}
  1289. PFullDebugBlockHeader = ^TFullDebugBlockHeader;
  1290. TFullDebugBlockHeader = packed record
  1291. {Space used by the medium block manager for previous/next block management.
  1292. If a medium block is binned then these two fields will be modified.}
  1293. Reserved1: Pointer;
  1294. Reserved2: Pointer;
  1295. {Is the block currently allocated? If it is allocated this will be the
  1296. address of the getmem routine through which it was allocated, otherwise it
  1297. will be nil.}
  1298. AllocatedByRoutine: Pointer;
  1299. {The allocation group: Can be used in the debugging process to group
  1300. related memory leaks together}
  1301. AllocationGroup: Cardinal;
  1302. {The allocation number: All new allocations are numbered sequentially. This
  1303. number may be useful in memory leak analysis. If it reaches 4G it wraps
  1304. back to 0.}
  1305. AllocationNumber: Cardinal;
  1306. {The call stack when the block was allocated}
  1307. AllocationStackTrace: TStackTrace;
  1308. {The thread that allocated the block}
  1309. AllocatedByThread: Cardinal;
  1310. {The thread that freed the block}
  1311. FreedByThread: Cardinal;
  1312. {The call stack when the block was freed}
  1313. FreeStackTrace: TStackTrace;
  1314. {The user requested size for the block. 0 if this is the first time the
  1315. block is used.}
  1316. UserSize: NativeUInt;
  1317. {The object class this block was used for the previous time it was
  1318. allocated. When a block is freed, the pointer that would normally be in the
  1319. space of the class pointer is copied here, so if it is detected that
  1320. the block was used after being freed we have an idea what class it is.}
  1321. PreviouslyUsedByClass: NativeUInt;
  1322. {The sum of all the dwords(32-bit)/qwords(64-bit) in this structure
  1323. excluding the initial two reserved fields and this field.}
  1324. HeaderCheckSum: NativeUInt;
  1325. end;
  1326. {The NativeUInt following the user area of the block is the inverse of
  1327. HeaderCheckSum. This is used to catch buffer overrun errors.}
  1328. {The class used to catch attempts to execute a virtual method of a freed
  1329. object}
  1330. TFreedObject = class
  1331. public
  1332. procedure GetVirtualMethodIndex;
  1333. procedure VirtualMethodError;
  1334. {$ifdef CatchUseOfFreedInterfaces}
  1335. procedure InterfaceError;
  1336. {$endif}
  1337. end;
  1338. {$ifdef FullDebugModeCallBacks}
  1339. {FullDebugMode memory manager event callbacks. Note that APHeaderFreedBlock in the TOnDebugFreeMemFinish
  1340. will not be valid for large (>260K) blocks.}
  1341. TOnDebugGetMemFinish = procedure(APHeaderNewBlock: PFullDebugBlockHeader; ASize: NativeInt);
  1342. TOnDebugFreeMemStart = procedure(APHeaderBlockToFree: PFullDebugBlockHeader);
  1343. TOnDebugFreeMemFinish = procedure(APHeaderFreedBlock: PFullDebugBlockHeader; AResult: Integer);
  1344. TOnDebugReallocMemStart = procedure(APHeaderBlockToReallocate: PFullDebugBlockHeader; ANewSize: NativeInt);
  1345. TOnDebugReallocMemFinish = procedure(APHeaderReallocatedBlock: PFullDebugBlockHeader; ANewSize: NativeInt);
  1346. var
  1347. {Note: FastMM will not catch exceptions inside these hooks, so make sure your hook code runs without
  1348. exceptions.}
  1349. OnDebugGetMemFinish: TOnDebugGetMemFinish = nil;
  1350. OnDebugFreeMemStart: TOnDebugFreeMemStart = nil;
  1351. OnDebugFreeMemFinish: TOnDebugFreeMemFinish = nil;
  1352. OnDebugReallocMemStart: TOnDebugReallocMemStart = nil;
  1353. OnDebugReallocMemFinish: TOnDebugReallocMemFinish = nil;
  1354. {$endif}
  1355. {$endif}
  1356. implementation
  1357. uses
  1358. {$ifndef Linux}
  1359. Windows,
  1360. {$ifdef FullDebugMode}
  1361. {$ifdef Delphi4or5}
  1362. ShlObj,
  1363. {$else}
  1364. SHFolder,
  1365. {$endif}
  1366. {$endif}
  1367. {$else}
  1368. Libc,
  1369. {$endif}
  1370. FastMM4Messages;
  1371. {Fixed size move procedures. The 64-bit versions assume 16-byte alignment.}
  1372. procedure Move4(const ASource; var ADest; ACount: NativeInt); forward;
  1373. procedure Move12(const ASource; var ADest; ACount: NativeInt); forward;
  1374. procedure Move20(const ASource; var ADest; ACount: NativeInt); forward;
  1375. procedure Move28(const ASource; var ADest; ACount: NativeInt); forward;
  1376. procedure Move36(const ASource; var ADest; ACount: NativeInt); forward;
  1377. procedure Move44(const ASource; var ADest; ACount: NativeInt); forward;
  1378. procedure Move52(const ASource; var ADest; ACount: NativeInt); forward;
  1379. procedure Move60(const ASource; var ADest; ACount: NativeInt); forward;
  1380. procedure Move68(const ASource; var ADest; ACount: NativeInt); forward;
  1381. {$ifdef DetectMMOperationsAfterUninstall}
  1382. {Invalid handlers to catch MM operations after uninstall}
  1383. function InvalidFreeMem(APointer: Pointer): Integer; forward;
  1384. function InvalidGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer; forward;
  1385. function InvalidReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer; forward;
  1386. function InvalidAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer; forward;
  1387. function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean; forward;
  1388. {$endif}
  1389. {-------------------------Private constants----------------------------}
  1390. const
  1391. {The size of a medium block pool. This is allocated through VirtualAlloc and
  1392. is used to serve medium blocks. The size must be a multiple of 16 and at
  1393. least 4 bytes less than a multiple of 4K (the page size) to prevent a
  1394. possible read access violation when reading past the end of a memory block
  1395. in the optimized move routine (MoveX16LP). In Full Debug mode we leave a
  1396. trailing 256 bytes to be able to safely do a memory dump.}
  1397. MediumBlockPoolSize = 20 * 64 * 1024{$ifndef FullDebugMode} - 16{$else} - 256{$endif};
  1398. {The granularity of small blocks}
  1399. {$ifdef Align16Bytes}
  1400. SmallBlockGranularity = 16;
  1401. {$else}
  1402. SmallBlockGranularity = 8;
  1403. {$endif}
  1404. {The granularity of medium blocks. Newly allocated medium blocks are
  1405. a multiple of this size plus MediumBlockSizeOffset, to avoid cache line
  1406. conflicts}
  1407. MediumBlockGranularity = 256;
  1408. MediumBlockSizeOffset = 48;
  1409. {The granularity of large blocks}
  1410. LargeBlockGranularity = 65536;
  1411. {The maximum size of a small block. Blocks Larger than this are either
  1412. medium or large blocks.}
  1413. MaximumSmallBlockSize = 2608;
  1414. {The smallest medium block size. (Medium blocks are rounded up to the nearest
  1415. multiple of MediumBlockGranularity plus MediumBlockSizeOffset)}
  1416. MinimumMediumBlockSize = 11 * 256 + MediumBlockSizeOffset;
  1417. {The number of bins reserved for medium blocks}
  1418. MediumBlockBinsPerGroup = 32;
  1419. MediumBlockBinGroupCount = 32;
  1420. MediumBlockBinCount = MediumBlockBinGroupCount * MediumBlockBinsPerGroup;
  1421. {The maximum size allocatable through medium blocks. Blocks larger than this
  1422. fall through to VirtualAlloc ( = large blocks).}
  1423. MaximumMediumBlockSize = MinimumMediumBlockSize + (MediumBlockBinCount - 1) * MediumBlockGranularity;
  1424. {The target number of small blocks per pool. The actual number of blocks per
  1425. pool may be much greater for very small sizes and less for larger sizes. The
  1426. cost of allocating the small block pool is amortized across all the small
  1427. blocks in the pool, however the blocks may not all end up being used so they
  1428. may be lying idle.}
  1429. TargetSmallBlocksPerPool = 48;
  1430. {The minimum number of small blocks per pool. Any available medium block must
  1431. have space for roughly this many small blocks (or more) to be useable as a
  1432. small block pool.}
  1433. MinimumSmallBlocksPerPool = 12;
  1434. {The lower and upper limits for the optimal small block pool size}
  1435. OptimalSmallBlockPoolSizeLowerLimit = 29 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
  1436. OptimalSmallBlockPoolSizeUpperLimit = 64 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
  1437. {The maximum small block pool size. If a free block is this size or larger
  1438. then it will be split.}
  1439. MaximumSmallBlockPoolSize = OptimalSmallBlockPoolSizeUpperLimit + MinimumMediumBlockSize;
  1440. {-------------Block type flags--------------}
  1441. {The lower 3 bits in the dword header of small blocks (4 bits in medium and
  1442. large blocks) are used as flags to indicate the state of the block}
  1443. {Set if the block is not in use}
  1444. IsFreeBlockFlag = 1;
  1445. {Set if this is a medium block}
  1446. IsMediumBlockFlag = 2;
  1447. {Set if it is a medium block being used as a small block pool. Only valid if
  1448. IsMediumBlockFlag is set.}
  1449. IsSmallBlockPoolInUseFlag = 4;
  1450. {Set if it is a large block. Only valid if IsMediumBlockFlag is not set.}
  1451. IsLargeBlockFlag = 4;
  1452. {Is the medium block preceding this block available? (Only used by medium
  1453. blocks)}
  1454. PreviousMediumBlockIsFreeFlag = 8;
  1455. {Is this large block segmented? I.e. is it actually built up from more than
  1456. one chunk allocated through VirtualAlloc? (Only used by large blocks.)}
  1457. LargeBlockIsSegmented = 8;
  1458. {The flags masks for small blocks}
  1459. DropSmallFlagsMask = -8;
  1460. ExtractSmallFlagsMask = 7;
  1461. {The flags masks for medium and large blocks}
  1462. DropMediumAndLargeFlagsMask = -16;
  1463. ExtractMediumAndLargeFlagsMask = 15;
  1464. {-------------Block resizing constants---------------}
  1465. SmallBlockDownsizeCheckAdder = 64;
  1466. SmallBlockUpsizeAdder = 32;
  1467. {When a medium block is reallocated to a size smaller than this, then it must
  1468. be reallocated to a small block and the data moved. If not, then it is
  1469. shrunk in place down to MinimumMediumBlockSize. Currently the limit is set
  1470. at a quarter of the minimum medium block size.}
  1471. MediumInPlaceDownsizeLimit = MinimumMediumBlockSize div 4;
  1472. {-------------Memory leak reporting constants---------------}
  1473. ExpectedMemoryLeaksListSize = 64 * 1024;
  1474. {-------------Other constants---------------}
  1475. {$ifndef NeverSleepOnThreadContention}
  1476. {Sleep time when a resource (small/medium/large block manager) is in use}
  1477. InitialSleepTime = 0;
  1478. {Used when the resource is still in use after the first sleep}
  1479. AdditionalSleepTime = 1;
  1480. {$endif}
  1481. {Hexadecimal characters}
  1482. HexTable: array[0..15] of AnsiChar = ('0', '1', '2', '3', '4', '5', '6', '7',
  1483. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
  1484. {Copyright message - not used anywhere in the code}
  1485. Copyright: AnsiString = 'FastMM4 (c) 2004 - 2011 Pierre le Riche / Professional Software Development';
  1486. {$ifdef FullDebugMode}
  1487. {Virtual Method Called On Freed Object Errors}
  1488. StandardVirtualMethodNames: array[1 + vmtParent div SizeOf(Pointer) .. -1] of PAnsiChar = (
  1489. {$ifdef BCB6OrDelphi6AndUp}
  1490. {$if RTLVersion >= 20}
  1491. 'Equals',
  1492. 'GetHashCode',
  1493. 'ToString',
  1494. {$ifend}
  1495. {$endif}
  1496. 'SafeCallException',
  1497. 'AfterConstruction',
  1498. 'BeforeDestruction',
  1499. 'Dispatch',
  1500. 'DefaultHandler',
  1501. 'NewInstance',
  1502. 'FreeInstance',
  1503. 'Destroy');
  1504. {The name of the FullDebugMode support DLL. The support DLL implements stack
  1505. tracing and the conversion of addresses to unit and line number information.}
  1506. {$ifdef 32Bit}
  1507. FullDebugModeLibraryName = FullDebugModeLibraryName32Bit;
  1508. {$else}
  1509. FullDebugModeLibraryName = FullDebugModeLibraryName64Bit;
  1510. {$endif}
  1511. {$endif}
  1512. {-------------------------Private types----------------------------}
  1513. type
  1514. {$ifdef Delphi4or5}
  1515. {Delphi 5 Compatibility}
  1516. PCardinal = ^Cardinal;
  1517. PPointer = ^Pointer;
  1518. {$endif}
  1519. {$ifdef BCB4}
  1520. {Define some additional types for BCB4}
  1521. PInteger = ^Integer;
  1522. {$endif}
  1523. {Move procedure type}
  1524. TMoveProc = procedure(const ASource; var ADest; ACount: NativeInt);
  1525. {Registers structure (for GetCPUID)}
  1526. TRegisters = record
  1527. RegEAX, RegEBX, RegECX, RegEDX: Integer;
  1528. end;
  1529. {The layout of a string allocation. Used to detect string leaks.}
  1530. PStrRec = ^StrRec;
  1531. StrRec = packed record
  1532. {$ifdef 64Bit}
  1533. _Padding: Integer;
  1534. {$endif}
  1535. {$ifdef BCB6OrDelphi6AndUp}
  1536. {$if RTLVersion >= 20}
  1537. codePage: Word;
  1538. elemSize: Word;
  1539. {$ifend}
  1540. {$endif}
  1541. refCnt: Integer;
  1542. length: Integer;
  1543. end;
  1544. {$ifdef EnableMemoryLeakReporting}
  1545. {Different kinds of memory leaks}
  1546. TMemoryLeakType = (mltUnexpectedLeak, mltExpectedLeakRegisteredByPointer,
  1547. mltExpectedLeakRegisteredByClass, mltExpectedLeakRegisteredBySize);
  1548. {$endif}
  1549. {---------------Small block structures-------------}
  1550. {Pointer to the header of a small block pool}
  1551. PSmallBlockPoolHeader = ^TSmallBlockPoolHeader;
  1552. {Small block type (Size = 32 bytes for 32-bit, 64 bytes for 64-bit).}
  1553. PSmallBlockType = ^TSmallBlockType;
  1554. TSmallBlockType = packed record
  1555. {True = Block type is locked}
  1556. BlockTypeLocked: Boolean;
  1557. {Bitmap indicating which of the first 8 medium block groups contain blocks
  1558. of a suitable size for a block pool.}
  1559. AllowedGroupsForBlockPoolBitmap: Byte;
  1560. {The block size for this block type}
  1561. BlockSize: Word;
  1562. {The minimum and optimal size of a small block pool for this block type}
  1563. MinimumBlockPoolSize: Word;
  1564. OptimalBlockPoolSize: Word;
  1565. {The first partially free pool for the given small block. This field must
  1566. be at the same offset as TSmallBlockPoolHeader.NextPartiallyFreePool.}
  1567. NextPartiallyFreePool: PSmallBlockPoolHeader;
  1568. {The last partially free pool for the small block type. This field must
  1569. be at the same offset as TSmallBlockPoolHeader.PreviousPartiallyFreePool.}
  1570. PreviousPartiallyFreePool: PSmallBlockPoolHeader;
  1571. {The offset of the last block that was served sequentially. The field must
  1572. be at the same offset as TSmallBlockPoolHeader.FirstFreeBlock.}
  1573. NextSequentialFeedBlockAddress: Pointer;
  1574. {The last block that can be served sequentially.}
  1575. MaxSequentialFeedBlockAddress: Pointer;
  1576. {The pool that is current being used to serve blocks in sequential order}
  1577. CurrentSequentialFeedPool: PSmallBlockPoolHeader;
  1578. {$ifdef UseCustomFixedSizeMoveRoutines}
  1579. {The fixed size move procedure used to move data for this block size when
  1580. it is upsized. When a block is downsized (which usually does not occur
  1581. that often) the variable size move routine is used.}
  1582. UpsizeMoveProcedure: TMoveProc;
  1583. {$else}
  1584. Reserved1: Pointer;
  1585. {$endif}
  1586. {$ifdef 64Bit}
  1587. {Pad to 64 bytes for 64-bit}
  1588. Reserved2: Pointer;
  1589. {$endif}
  1590. end;
  1591. {Small block pool (Size = 32 bytes for 32-bit, 48 bytes for 64-bit).}
  1592. TSmallBlockPoolHeader = packed record
  1593. {BlockType}
  1594. BlockType: PSmallBlockType;
  1595. {$ifdef 32Bit}
  1596. {Align the next fields to the same fields in TSmallBlockType and pad this
  1597. structure to 32 bytes for 32-bit}
  1598. Reserved1: Cardinal;
  1599. {$endif}
  1600. {The next and previous pool that has free blocks of this size. Do not
  1601. change the position of these two fields: They must be at the same offsets
  1602. as the fields in TSmallBlockType of the same name.}
  1603. NextPartiallyFreePool: PSmallBlockPoolHeader;
  1604. PreviousPartiallyFreePool: PSmallBlockPoolHeader;
  1605. {Pointer to the first free block inside this pool. This field must be at
  1606. the same offset as TSmallBlockType.NextSequentialFeedBlockAddress.}
  1607. FirstFreeBlock: Pointer;
  1608. {The number of blocks allocated in this pool.}
  1609. BlocksInUse: Cardinal;
  1610. {Padding}
  1611. Reserved2: Cardinal;
  1612. {The pool pointer and flags of the first block}
  1613. FirstBlockPoolPointerAndFlags: NativeUInt;
  1614. end;
  1615. {Small block layout:
  1616. At offset -SizeOf(Pointer) = Flags + address of the small block pool.
  1617. At offset BlockSize - SizeOf(Pointer) = Flags + address of the small block
  1618. pool for the next small block.
  1619. }
  1620. {------------------------Medium block structures------------------------}
  1621. {The medium block pool from which medium blocks are drawn. Size = 16 bytes
  1622. for 32-bit and 32 bytes for 64-bit.}
  1623. PMediumBlockPoolHeader = ^TMediumBlockPoolHeader;
  1624. TMediumBlockPoolHeader = packed record
  1625. {Points to the previous and next medium block pools. This circular linked
  1626. list is used to track memory leaks on program shutdown.}
  1627. PreviousMediumBlockPoolHeader: PMediumBlockPoolHeader;
  1628. NextMediumBlockPoolHeader: PMediumBlockPoolHeader;
  1629. {Padding}
  1630. Reserved1: NativeUInt;
  1631. {The block size and flags of the first medium block in the block pool}
  1632. FirstMediumBlockSizeAndFlags: NativeUInt;
  1633. end;
  1634. {Medium block layout:
  1635. Offset: -2 * SizeOf(Pointer) = Previous Block Size (only if the previous block is free)
  1636. Offset: -SizeOf(Pointer) = This block size and flags
  1637. Offset: 0 = User data / Previous Free Block (if this block is free)
  1638. Offset: SizeOf(Pointer) = Next Free Block (if this block is free)
  1639. Offset: BlockSize - 2*SizeOf(Pointer) = Size of this block (if this block is free)
  1640. Offset: BlockSize - SizeOf(Pointer) = Size of the next block and flags
  1641. {A medium block that is unused}
  1642. PMediumFreeBlock = ^TMediumFreeBlock;
  1643. TMediumFreeBlock = packed record
  1644. PreviousFreeBlock: PMediumFreeBlock;
  1645. NextFreeBlock: PMediumFreeBlock;
  1646. end;
  1647. {-------------------------Large block structures------------------------}
  1648. {Large block header record (Size = 16 for 32-bit, 32 for 64-bit)}
  1649. PLargeBlockHeader = ^TLargeBlockHeader;
  1650. TLargeBlockHeader = packed record
  1651. {Points to the previous and next large blocks. This circular linked
  1652. list is used to track memory leaks on program shutdown.}
  1653. PreviousLargeBlockHeader: PLargeBlockHeader;
  1654. NextLargeBlockHeader: PLargeBlockHeader;
  1655. {The user allocated size of the Large block}
  1656. UserAllocatedSize: NativeUInt;
  1657. {The size of this block plus the flags}
  1658. BlockSizeAndFlags: NativeUInt;
  1659. end;
  1660. {-------------------------Expected Memory Leak Structures--------------------}
  1661. {$ifdef EnableMemoryLeakReporting}
  1662. {The layout of an expected leak. All fields may not be specified, in which
  1663. case it may be harder to determine which leaks are expected and which are
  1664. not.}
  1665. PExpectedMemoryLeak = ^TExpectedMemoryLeak;
  1666. PPExpectedMemoryLeak = ^PExpectedMemoryLeak;
  1667. TExpectedMemoryLeak = packed record
  1668. {Linked list pointers}
  1669. PreviousLeak, NextLeak: PExpectedMemoryLeak;
  1670. {Information about the expected leak}
  1671. LeakAddress: Pointer;
  1672. LeakedClass: TClass;
  1673. {$ifdef CheckCppObjectTypeEnabled}
  1674. LeakedCppTypeIdPtr: Pointer;
  1675. {$endif}
  1676. LeakSize: NativeInt;
  1677. LeakCount: Integer;
  1678. end;
  1679. TExpectedMemoryLeaks = packed record
  1680. {The number of entries used in the expected leaks buffer}
  1681. EntriesUsed: Integer;
  1682. {Freed entries}
  1683. FirstFreeSlot: PExpectedMemoryLeak;
  1684. {Entries with the address specified}
  1685. FirstEntryByAddress: PExpectedMemoryLeak;
  1686. {Entries with no address specified, but with the class specified}
  1687. FirstEntryByClass: PExpectedMemoryLeak;
  1688. {Entries with only size specified}
  1689. FirstEntryBySizeOnly: PExpectedMemoryLeak;
  1690. {The expected leaks buffer (Need to leave space for this header)}
  1691. ExpectedLeaks: packed array[0..(ExpectedMemoryLeaksListSize - 64) div SizeOf(TExpectedMemoryLeak) - 1] of TExpectedMemoryLeak;
  1692. end;
  1693. PExpectedMemoryLeaks = ^TExpectedMemoryLeaks;
  1694. {$endif}
  1695. {-------------------------Private constants----------------------------}
  1696. const
  1697. {$ifndef BCB6OrDelphi7AndUp}
  1698. reOutOfMemory = 1;
  1699. reInvalidPtr = 2;
  1700. {$endif}
  1701. {The size of the block header in front of small and medium blocks}
  1702. BlockHeaderSize = SizeOf(Pointer);
  1703. {The size of a small block pool header}
  1704. SmallBlockPoolHeaderSize = SizeOf(TSmallBlockPoolHeader);
  1705. {The size of a medium block pool header}
  1706. MediumBlockPoolHeaderSize = SizeOf(TMediumBlockPoolHeader);
  1707. {The size of the header in front of Large blocks}
  1708. LargeBlockHeaderSize = SizeOf(TLargeBlockHeader);
  1709. {$ifdef FullDebugMode}
  1710. {We need space for the header, the trailer checksum and the trailing block
  1711. size (only used by freed medium blocks).}
  1712. FullDebugBlockOverhead = SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt) + SizeOf(Pointer);
  1713. {$endif}
  1714. {-------------------------Private variables----------------------------}
  1715. var
  1716. {-----------------Small block management------------------}
  1717. {The small block types. Sizes include the leading header. Sizes are
  1718. picked to limit maximum wastage to about 10% or 256 bytes (whichever is
  1719. less) where possible.}
  1720. SmallBlockTypes: packed array[0..NumSmallBlockTypes - 1] of TSmallBlockType =(
  1721. {8/16 byte jumps}
  1722. {$ifndef Align16Bytes}
  1723. (BlockSize: 8 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move4{$endif}),
  1724. {$endif}
  1725. (BlockSize: 16 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move12{$endif}),
  1726. {$ifndef Align16Bytes}
  1727. (BlockSize: 24 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move20{$endif}),
  1728. {$endif}
  1729. (BlockSize: 32 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move28{$endif}),
  1730. {$ifndef Align16Bytes}
  1731. (BlockSize: 40 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move36{$endif}),
  1732. {$endif}
  1733. (BlockSize: 48 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move44{$endif}),
  1734. {$ifndef Align16Bytes}
  1735. (BlockSize: 56 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move52{$endif}),
  1736. {$endif}
  1737. (BlockSize: 64 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move60{$endif}),
  1738. {$ifndef Align16Bytes}
  1739. (BlockSize: 72 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move68{$endif}),
  1740. {$endif}
  1741. (BlockSize: 80),
  1742. {$ifndef Align16Bytes}
  1743. (BlockSize: 88),
  1744. {$endif}
  1745. (BlockSize: 96),
  1746. {$ifndef Align16Bytes}
  1747. (BlockSize: 104),
  1748. {$endif}
  1749. (BlockSize: 112),
  1750. {$ifndef Align16Bytes}
  1751. (BlockSize: 120),
  1752. {$endif}
  1753. (BlockSize: 128),
  1754. {$ifndef Align16Bytes}
  1755. (BlockSize: 136),
  1756. {$endif}
  1757. (BlockSize: 144),
  1758. {$ifndef Align16Bytes}
  1759. (BlockSize: 152),
  1760. {$endif}
  1761. (BlockSize: 160),
  1762. {16 byte jumps}
  1763. (BlockSize: 176),
  1764. (BlockSize: 192),
  1765. (BlockSize: 208),
  1766. (BlockSize: 224),
  1767. (BlockSize: 240),
  1768. (BlockSize: 256),
  1769. (BlockSize: 272),
  1770. (BlockSize: 288),
  1771. (BlockSize: 304),
  1772. (BlockSize: 320),
  1773. {32 byte jumps}
  1774. (BlockSize: 352),
  1775. (BlockSize: 384),
  1776. (BlockSize: 416),
  1777. (BlockSize: 448),
  1778. (BlockSize: 480),
  1779. {48 byte jumps}
  1780. (BlockSize: 528),
  1781. (BlockSize: 576),
  1782. (BlockSize: 624),
  1783. (BlockSize: 672),
  1784. {64 byte jumps}
  1785. (BlockSize: 736),
  1786. (BlockSize: 800),
  1787. {80 byte jumps}
  1788. (BlockSize: 880),
  1789. (BlockSize: 960),
  1790. {96 byte jumps}
  1791. (BlockSize: 1056),
  1792. (BlockSize: 1152),
  1793. {112 byte jumps}
  1794. (BlockSize: 1264),
  1795. (BlockSize: 1376),
  1796. {128 byte jumps}
  1797. (BlockSize: 1504),
  1798. {144 byte jumps}
  1799. (BlockSize: 1648),
  1800. {160 byte jumps}
  1801. (BlockSize: 1808),
  1802. {176 byte jumps}
  1803. (BlockSize: 1984),
  1804. {192 byte jumps}
  1805. (BlockSize: 2176),
  1806. {208 byte jumps}
  1807. (BlockSize: 2384),
  1808. {224 byte jumps}
  1809. (BlockSize: MaximumSmallBlockSize),
  1810. {The last block size occurs three times. If, during a GetMem call, the
  1811. requested block size is already locked by another thread then up to two
  1812. larger block sizes may be used instead. Having the last block size occur
  1813. three times avoids the need to have a size overflow check.}
  1814. (BlockSize: MaximumSmallBlockSize),
  1815. (BlockSize: MaximumSmallBlockSize));
  1816. {Size to small block type translation table}
  1817. AllocSize2SmallBlockTypeIndX4: packed array[0..(MaximumSmallBlockSize - 1) div SmallBlockGranularity] of Byte;
  1818. {-----------------Medium block management------------------}
  1819. {A dummy medium block pool header: Maintains a circular list of all medium
  1820. block pools to enable memory leak detection on program shutdown.}
  1821. MediumBlockPoolsCircularList: TMediumBlockPoolHeader;
  1822. {Are medium blocks locked?}
  1823. MediumBlocksLocked: Boolean;
  1824. {The sequential feed medium block pool.}
  1825. LastSequentiallyFedMediumBlock: Pointer;
  1826. MediumSequentialFeedBytesLeft: Cardinal;
  1827. {The medium block bins are divided into groups of 32 bins. If a bit
  1828. is set in this group bitmap, then at least one bin in the group has free
  1829. blocks.}
  1830. MediumBlockBinGroupBitmap: Cardinal;
  1831. {The medium block bins: total of 32 * 32 = 1024 bins of a certain
  1832. minimum size.}
  1833. MediumBlockBinBitmaps: packed array[0..MediumBlockBinGroupCount - 1] of Cardinal;
  1834. {The medium block bins. There are 1024 LIFO circular linked lists each
  1835. holding blocks of a specified minimum size. The sizes vary in size from
  1836. MinimumMediumBlockSize to MaximumMediumBlockSize. The bins are treated as
  1837. type TMediumFreeBlock to avoid pointer checks.}
  1838. MediumBlockBins: packed array[0..MediumBlockBinCount - 1] of TMediumFreeBlock;
  1839. {-----------------Large block management------------------}
  1840. {Are large blocks locked?}
  1841. LargeBlocksLocked: Boolean;
  1842. {A dummy large block header: Maintains a list of all allocated large blocks
  1843. to enable memory leak detection on program shutdown.}
  1844. LargeBlocksCircularList: TLargeBlockHeader;
  1845. {-------------------------Expected Memory Leak Structures--------------------}
  1846. {$ifdef EnableMemoryLeakReporting}
  1847. {The expected memory leaks}
  1848. ExpectedMemoryLeaks: PExpectedMemoryLeaks;
  1849. ExpectedMemoryLeaksListLocked: Boolean;
  1850. {$endif}
  1851. {---------------------Full Debug Mode structures--------------------}
  1852. {$ifdef FullDebugMode}
  1853. {The allocation group stack}
  1854. AllocationGroupStack: array[0..AllocationGroupStackSize - 1] of Cardinal;
  1855. {The allocation group stack top (it is an index into AllocationGroupStack)}
  1856. AllocationGroupStackTop: Cardinal;
  1857. {The last allocation number used}
  1858. CurrentAllocationNumber: Cardinal;
  1859. {This is a count of the number of threads currently inside any of the
  1860. FullDebugMode GetMem, Freemem or ReallocMem handlers. If this value
  1861. is negative then a block scan is in progress and no thread may
  1862. allocate, free or reallocate any block or modify any FullDebugMode
  1863. block header or footer.}
  1864. ThreadsInFullDebugModeRoutine: Integer;
  1865. {The current log file name}
  1866. MMLogFileName: array[0..1023] of AnsiChar;
  1867. {The 64K block of reserved memory used to trap invalid memory accesses using
  1868. fields in a freed object.}
  1869. ReservedBlock: Pointer;
  1870. {The virtual method index count - used to get the virtual method index for a
  1871. virtual method call on a freed object.}
  1872. VMIndex: Integer;
  1873. {The fake VMT used to catch virtual method calls on freed objects.}
  1874. FreedObjectVMT: packed record
  1875. VMTData: array[vmtSelfPtr .. vmtParent + SizeOf(Pointer) - 1] of byte;
  1876. VMTMethods: array[vmtParent + SizeOf(Pointer) .. vmtParent + MaxFakeVMTEntries * SizeOf(Pointer) + SizeOf(Pointer) - 1] of Byte;
  1877. end;
  1878. {$ifdef CatchUseOfFreedInterfaces}
  1879. VMTBadInterface: array[0..MaxFakeVMTEntries - 1] of Pointer;
  1880. {$endif}
  1881. {$endif}
  1882. {--------------Other info--------------}
  1883. {The memory manager that was replaced}
  1884. OldMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif};
  1885. {The replacement memory manager}
  1886. NewMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif};
  1887. {$ifdef DetectMMOperationsAfterUninstall}
  1888. {Invalid handlers to catch MM operations after uninstall}
  1889. InvalidMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif} = (
  1890. GetMem: InvalidGetMem;
  1891. FreeMem: InvalidFreeMem;
  1892. ReallocMem: InvalidReallocMem
  1893. {$ifdef BDS2006AndUp};
  1894. AllocMem: InvalidAllocMem;
  1895. RegisterExpectedMemoryLeak: InvalidRegisterAndUnRegisterMemoryLeak;
  1896. UnRegisterExpectedMemoryLeak: InvalidRegisterAndUnRegisterMemoryLeak;
  1897. {$endif}
  1898. );
  1899. {$endif}
  1900. {$ifdef MMSharingEnabled}
  1901. {A string uniquely identifying the current process (for sharing the memory
  1902. manager between DLLs and the main application)}
  1903. MappingObjectName: array[0..25] of AnsiChar = ('L', 'o', 'c', 'a', 'l', '\',
  1904. 'F', 'a', 's', 't', 'M', 'M', '_', 'P', 'I', 'D', '_', '?', '?', '?', '?',
  1905. '?', '?', '?', '?', #0);
  1906. {$ifdef EnableBackwardCompatibleMMSharing}
  1907. UniqueProcessIDString: array[1..20] of AnsiChar = ('?', '?', '?', '?', '?',
  1908. '?', '?', '?', '_', 'P', 'I', 'D', '_', 'F', 'a', 's', 't', 'M', 'M', #0);
  1909. UniqueProcessIDStringBE: array[1..23] of AnsiChar = ('?', '?', '?', '?', '?',
  1910. '?', '?', '?', '_', 'P', 'I', 'D', '_', 'F', 'a', 's', 't', 'M', 'M', '_',
  1911. 'B', 'E', #0);
  1912. {The handle of the MM window}
  1913. MMWindow: HWND;
  1914. {The handle of the MM window (for default MM of Delphi 2006 compatibility)}
  1915. MMWindowBE: HWND;
  1916. {$endif}
  1917. {The handle of the memory mapped file}
  1918. MappingObjectHandle: NativeUInt;
  1919. {$endif}
  1920. {Has FastMM been installed?}
  1921. FastMMIsInstalled: Boolean;
  1922. {Is the MM in place a shared memory manager?}
  1923. IsMemoryManagerOwner: Boolean;
  1924. {Must MMX be used for move operations?}
  1925. {$ifdef EnableMMX}
  1926. {$ifndef ForceMMX}
  1927. UseMMX: Boolean;
  1928. {$endif}
  1929. {$endif}
  1930. {Is a MessageBox currently showing? If so, do not show another one.}
  1931. ShowingMessageBox: Boolean;
  1932. {True if RunInitializationCode has been called already.}
  1933. InitializationCodeHasRun: Boolean = False;
  1934. {----------------Utility Functions------------------}
  1935. {A copy of StrLen in order to avoid the SysUtils unit, which would have
  1936. introduced overhead like exception handling code.}
  1937. function StrLen(const AStr: PAnsiChar): NativeUInt;
  1938. {$ifndef Use32BitAsm}
  1939. begin
  1940. Result := 0;
  1941. while AStr[Result] <> #0 do
  1942. Inc(Result);
  1943. end;
  1944. {$else}
  1945. asm
  1946. {Check the first byte}
  1947. cmp byte ptr [eax], 0
  1948. je @ZeroLength
  1949. {Get the negative of the string start in edx}
  1950. mov edx, eax
  1951. neg edx
  1952. {Word align}
  1953. add eax, 1
  1954. and eax, -2
  1955. @ScanLoop:
  1956. mov cx, [eax]
  1957. add eax, 2
  1958. test cl, ch
  1959. jnz @ScanLoop
  1960. test cl, cl
  1961. jz @ReturnLess2
  1962. test ch, ch
  1963. jnz @ScanLoop
  1964. lea eax, [eax + edx - 1]
  1965. ret
  1966. @ReturnLess2:
  1967. lea eax, [eax + edx - 2]
  1968. ret
  1969. @ZeroLength:
  1970. xor eax, eax
  1971. end;
  1972. {$endif}
  1973. {$ifdef EnableMMX}
  1974. {$ifndef ForceMMX}
  1975. {Returns true if the CPUID instruction is supported}
  1976. function CPUID_Supported: Boolean;
  1977. asm
  1978. pushfd
  1979. pop eax
  1980. mov edx, eax
  1981. xor eax, $200000
  1982. push eax
  1983. popfd
  1984. pushfd
  1985. pop eax
  1986. xor eax, edx
  1987. setnz al
  1988. end;
  1989. {Gets the CPUID}
  1990. function GetCPUID(AInfoRequired: Integer): TRegisters;
  1991. asm
  1992. push ebx
  1993. push esi
  1994. mov esi, edx
  1995. {cpuid instruction}
  1996. {$ifdef Delphi4or5}
  1997. db $0f, $a2
  1998. {$else}
  1999. cpuid
  2000. {$endif}
  2001. {Save registers}
  2002. mov TRegisters[esi].RegEAX, eax
  2003. mov TRegisters[esi].RegEBX, ebx
  2004. mov TRegisters[esi].RegECX, ecx
  2005. mov TRegisters[esi].RegEDX, edx
  2006. pop esi
  2007. pop ebx
  2008. end;
  2009. {Returns true if the CPU supports MMX}
  2010. function MMX_Supported: Boolean;
  2011. var
  2012. LReg: TRegisters;
  2013. begin
  2014. if CPUID_Supported then
  2015. begin
  2016. {Get the CPUID}
  2017. LReg := GetCPUID(1);
  2018. {Bit 23 must be set for MMX support}
  2019. Result := LReg.RegEDX and $800000 <> 0;
  2020. end
  2021. else
  2022. Result := False;
  2023. end;
  2024. {$endif}
  2025. {$endif}
  2026. {Compare [AAddress], CompareVal:
  2027. If Equal: [AAddress] := NewVal and result = CompareVal
  2028. If Unequal: Result := [AAddress]}
  2029. function LockCmpxchg(CompareVal, NewVal: Byte; AAddress: PByte): Byte;
  2030. asm
  2031. {$ifdef 32Bit}
  2032. {On entry:
  2033. al = CompareVal,
  2034. dl = NewVal,
  2035. ecx = AAddress}
  2036. {$ifndef Linux}
  2037. lock cmpxchg [ecx], dl
  2038. {$else}
  2039. {Workaround for Kylix compiler bug}
  2040. db $F0, $0F, $B0, $11
  2041. {$endif}
  2042. {$else}
  2043. {On entry:
  2044. cl = CompareVal
  2045. dl = NewVal
  2046. r8 = AAddress}
  2047. .noframe
  2048. mov rax, rcx
  2049. lock cmpxchg [r8], dl
  2050. {$endif}
  2051. end;
  2052. {$ifndef ASMVersion}
  2053. {Gets the first set bit in the 32-bit number, returning the bit index}
  2054. function FindFirstSetBit(ACardinal: Cardinal): Cardinal;
  2055. asm
  2056. {$ifdef 64Bit}
  2057. .noframe
  2058. mov rax, rcx
  2059. {$endif}
  2060. bsf eax, eax
  2061. end;
  2062. {$endif}
  2063. {Writes the module filename to the specified buffer and returns the number of
  2064. characters written.}
  2065. function AppendModuleFileName(ABuffer: PAnsiChar): Integer;
  2066. var
  2067. LModuleHandle: HModule;
  2068. begin
  2069. {Get the module handle}
  2070. {$ifndef borlndmmdll}
  2071. if IsLibrary then
  2072. LModuleHandle := HInstance
  2073. else
  2074. {$endif}
  2075. LModuleHandle := 0;
  2076. {Get the module name}
  2077. {$ifndef Linux}
  2078. Result := GetModuleFileNameA(LModuleHandle, ABuffer, 512);
  2079. {$else}
  2080. Result := GetModuleFileName(LModuleHandle, ABuffer, 512);
  2081. {$endif}
  2082. end;
  2083. {Copies the name of the module followed by the given string to the buffer,
  2084. returning the pointer following the buffer.}
  2085. function AppendStringToModuleName(AString, ABuffer: PAnsiChar): PAnsiChar;
  2086. var
  2087. LModuleNameLength: Cardinal;
  2088. LCopyStart: PAnsiChar;
  2089. begin
  2090. {Get the name of the application}
  2091. LModuleNameLength := AppendModuleFileName(ABuffer);
  2092. {Replace the last few characters}
  2093. if LModuleNameLength > 0 then
  2094. begin
  2095. {Find the last backslash}
  2096. LCopyStart := PAnsiChar(PByte(ABuffer) + LModuleNameLength - 1);
  2097. LModuleNameLength := 0;
  2098. while (UIntPtr(LCopyStart) >= UIntPtr(ABuffer))
  2099. and (LCopyStart^ <> '\') do
  2100. begin
  2101. Inc(LModuleNameLength);
  2102. Dec(LCopyStart);
  2103. end;
  2104. {Copy the name to the start of the buffer}
  2105. Inc(LCopyStart);
  2106. System.Move(LCopyStart^, ABuffer^, LModuleNameLength);
  2107. Inc(ABuffer, LModuleNameLength);
  2108. ABuffer^ := ':';
  2109. Inc(ABuffer);
  2110. ABuffer^ := ' ';
  2111. Inc(ABuffer);
  2112. end;
  2113. {Append the string}
  2114. while AString^ <> #0 do
  2115. begin
  2116. ABuffer^ := AString^;
  2117. Inc(ABuffer);
  2118. {Next char}
  2119. Inc(AString);
  2120. end;
  2121. ABuffer^ := #0;
  2122. Result := ABuffer;
  2123. end;
  2124. {----------------Faster Move Procedures-------------------}
  2125. {Fixed size move operations ignore the size parameter. All moves are assumed to
  2126. be non-overlapping.}
  2127. procedure Move4(const ASource; var ADest; ACount: NativeInt);
  2128. asm
  2129. {$ifdef 32Bit}
  2130. mov eax, [eax]
  2131. mov [edx], eax
  2132. {$else}
  2133. .noframe
  2134. mov eax, [rcx]
  2135. mov [rdx], eax
  2136. {$endif}
  2137. end;
  2138. procedure Move12(const ASource; var ADest; ACount: NativeInt);
  2139. asm
  2140. {$ifdef 32Bit}
  2141. mov ecx, [eax]
  2142. mov [edx], ecx
  2143. mov ecx, [eax + 4]
  2144. mov eax, [eax + 8]
  2145. mov [edx + 4], ecx
  2146. mov [edx + 8], eax
  2147. {$else}
  2148. .noframe
  2149. mov rax, [rcx]
  2150. mov ecx, [rcx + 8]
  2151. mov [rdx], rax
  2152. mov [rdx + 8], ecx
  2153. {$endif}
  2154. end;
  2155. procedure Move20(const ASource; var ADest; ACount: NativeInt);
  2156. asm
  2157. {$ifdef 32Bit}
  2158. mov ecx, [eax]
  2159. mov [edx], ecx
  2160. mov ecx, [eax + 4]
  2161. mov [edx + 4], ecx
  2162. mov ecx, [eax + 8]
  2163. mov [edx + 8], ecx
  2164. mov ecx, [eax + 12]
  2165. mov eax, [eax + 16]
  2166. mov [edx + 12], ecx
  2167. mov [edx + 16], eax
  2168. {$else}
  2169. .noframe
  2170. movdqa xmm0, [rcx]
  2171. mov ecx, [rcx + 16]
  2172. movdqa [rdx], xmm0
  2173. mov [rdx + 16], ecx
  2174. {$endif}
  2175. end;
  2176. procedure Move28(const ASource; var ADest; ACount: NativeInt);
  2177. asm
  2178. {$ifdef 32Bit}
  2179. mov ecx, [eax]
  2180. mov [edx], ecx
  2181. mov ecx, [eax + 4]
  2182. mov [edx + 4], ecx
  2183. mov ecx, [eax + 8]
  2184. mov [edx + 8], ecx
  2185. mov ecx, [eax + 12]
  2186. mov [edx + 12], ecx
  2187. mov ecx, [eax + 16]
  2188. mov [edx + 16], ecx
  2189. mov ecx, [eax + 20]
  2190. mov eax, [eax + 24]
  2191. mov [edx + 20], ecx
  2192. mov [edx + 24], eax
  2193. {$else}
  2194. .noframe
  2195. movdqa xmm0, [rcx]
  2196. mov r8, [rcx + 16]
  2197. mov ecx, [rcx + 24]
  2198. movdqa [rdx], xmm0
  2199. mov [rdx + 16], r8
  2200. mov [rdx + 24], ecx
  2201. {$endif}
  2202. end;
  2203. procedure Move36(const ASource; var ADest; ACount: NativeInt);
  2204. asm
  2205. {$ifdef 32Bit}
  2206. fild qword ptr [eax]
  2207. fild qword ptr [eax + 8]
  2208. fild qword ptr [eax + 16]
  2209. fild qword ptr [eax + 24]
  2210. mov ecx, [eax + 32]
  2211. mov [edx + 32], ecx
  2212. fistp qword ptr [edx + 24]
  2213. fistp qword ptr [edx + 16]
  2214. fistp qword ptr [edx + 8]
  2215. fistp qword ptr [edx]
  2216. {$else}
  2217. .noframe
  2218. movdqa xmm0, [rcx]
  2219. movdqa xmm1, [rcx + 16]
  2220. mov ecx, [rcx + 32]
  2221. movdqa [rdx], xmm0
  2222. movdqa [rdx + 16], xmm1
  2223. mov [rdx + 32], ecx
  2224. {$endif}
  2225. end;
  2226. procedure Move44(const ASource; var ADest; ACount: NativeInt);
  2227. asm
  2228. {$ifdef 32Bit}
  2229. fild qword ptr [eax]
  2230. fild qword ptr [eax + 8]
  2231. fild qword ptr [eax + 16]
  2232. fild qword ptr [eax + 24]
  2233. fild qword ptr [eax + 32]
  2234. mov ecx, [eax + 40]
  2235. mov [edx + 40], ecx
  2236. fistp qword ptr [edx + 32]
  2237. fistp qword ptr [edx + 24]
  2238. fistp qword ptr [edx + 16]
  2239. fistp qword ptr [edx + 8]
  2240. fistp qword ptr [edx]
  2241. {$else}
  2242. .noframe
  2243. movdqa xmm0, [rcx]
  2244. movdqa xmm1, [rcx + 16]
  2245. mov r8, [rcx + 32]
  2246. mov ecx, [rcx + 40]
  2247. movdqa [rdx], xmm0
  2248. movdqa [rdx + 16], xmm1
  2249. mov [rdx + 32], r8
  2250. mov [rdx + 40], ecx
  2251. {$endif}
  2252. end;
  2253. procedure Move52(const ASource; var ADest; ACount: NativeInt);
  2254. asm
  2255. {$ifdef 32Bit}
  2256. fild qword ptr [eax]
  2257. fild qword ptr [eax + 8]
  2258. fild qword ptr [eax + 16]
  2259. fild qword ptr [eax + 24]
  2260. fild qword ptr [eax + 32]
  2261. fild qword ptr [eax + 40]
  2262. mov ecx, [eax + 48]
  2263. mov [edx + 48], ecx
  2264. fistp qword ptr [edx + 40]
  2265. fistp qword ptr [edx + 32]
  2266. fistp qword ptr [edx + 24]
  2267. fistp qword ptr [edx + 16]
  2268. fistp qword ptr [edx + 8]
  2269. fistp qword ptr [edx]
  2270. {$else}
  2271. .noframe
  2272. movdqa xmm0, [rcx]
  2273. movdqa xmm1, [rcx + 16]
  2274. movdqa xmm2, [rcx + 32]
  2275. mov ecx, [rcx + 48]
  2276. movdqa [rdx], xmm0
  2277. movdqa [rdx + 16], xmm1
  2278. movdqa [rdx + 32], xmm2
  2279. mov [rdx + 48], ecx
  2280. {$endif}
  2281. end;
  2282. procedure Move60(const ASource; var ADest; ACount: NativeInt);
  2283. asm
  2284. {$ifdef 32Bit}
  2285. fild qword ptr [eax]
  2286. fild qword ptr [eax + 8]
  2287. fild qword ptr [eax + 16]
  2288. fild qword ptr [eax + 24]
  2289. fild qword ptr [eax + 32]
  2290. fild qword ptr [eax + 40]
  2291. fild qword ptr [eax + 48]
  2292. mov ecx, [eax + 56]
  2293. mov [edx + 56], ecx
  2294. fistp qword ptr [edx + 48]
  2295. fistp qword ptr [edx + 40]
  2296. fistp qword ptr [edx + 32]
  2297. fistp qword ptr [edx + 24]
  2298. fistp qword ptr [edx + 16]
  2299. fistp qword ptr [edx + 8]
  2300. fistp qword ptr [edx]
  2301. {$else}
  2302. .noframe
  2303. movdqa xmm0, [rcx]
  2304. movdqa xmm1, [rcx + 16]
  2305. movdqa xmm2, [rcx + 32]
  2306. mov r8, [rcx + 48]
  2307. mov ecx, [rcx + 56]
  2308. movdqa [rdx], xmm0
  2309. movdqa [rdx + 16], xmm1
  2310. movdqa [rdx + 32], xmm2
  2311. mov [rdx + 48], r8
  2312. mov [rdx + 56], ecx
  2313. {$endif}
  2314. end;
  2315. procedure Move68(const ASource; var ADest; ACount: NativeInt);
  2316. asm
  2317. {$ifdef 32Bit}
  2318. fild qword ptr [eax]
  2319. fild qword ptr [eax + 8]
  2320. fild qword ptr [eax + 16]
  2321. fild qword ptr [eax + 24]
  2322. fild qword ptr [eax + 32]
  2323. fild qword ptr [eax + 40]
  2324. fild qword ptr [eax + 48]
  2325. fild qword ptr [eax + 56]
  2326. mov ecx, [eax + 64]
  2327. mov [edx + 64], ecx
  2328. fistp qword ptr [edx + 56]
  2329. fistp qword ptr [edx + 48]
  2330. fistp qword ptr [edx + 40]
  2331. fistp qword ptr [edx + 32]
  2332. fistp qword ptr [edx + 24]
  2333. fistp qword ptr [edx + 16]
  2334. fistp qword ptr [edx + 8]
  2335. fistp qword ptr [edx]
  2336. {$else}
  2337. .noframe
  2338. movdqa xmm0, [rcx]
  2339. movdqa xmm1, [rcx + 16]
  2340. movdqa xmm2, [rcx + 32]
  2341. movdqa xmm3, [rcx + 48]
  2342. mov ecx, [rcx + 64]
  2343. movdqa [rdx], xmm0
  2344. movdqa [rdx + 16], xmm1
  2345. movdqa [rdx + 32], xmm2
  2346. movdqa [rdx + 48], xmm3
  2347. mov [rdx + 64], ecx
  2348. {$endif}
  2349. end;
  2350. {Variable size move procedure: Rounds ACount up to the next multiple of 16 less
  2351. SizeOf(Pointer). Important note: Always moves at least 16 - SizeOf(Pointer)
  2352. bytes (the minimum small block size with 16 byte alignment), irrespective of
  2353. ACount.}
  2354. procedure MoveX16LP(const ASource; var ADest; ACount: NativeInt);
  2355. asm
  2356. {$ifdef 32Bit}
  2357. {Make the counter negative based: The last 12 bytes are moved separately}
  2358. sub ecx, 12
  2359. add eax, ecx
  2360. add edx, ecx
  2361. {$ifdef EnableMMX}
  2362. {$ifndef ForceMMX}
  2363. cmp UseMMX, True
  2364. jne @FPUMove
  2365. {$endif}
  2366. {Make the counter negative based: The last 12 bytes are moved separately}
  2367. neg ecx
  2368. jns @MMXMoveLast12
  2369. @MMXMoveLoop:
  2370. {Move a 16 byte block}
  2371. {$ifdef Delphi4or5}
  2372. {Delphi 5 compatibility}
  2373. db $0f, $6f, $04, $01
  2374. db $0f, $6f, $4c, $01, $08
  2375. db $0f, $7f, $04, $11
  2376. db $0f, $7f, $4c, $11, $08
  2377. {$else}
  2378. movq mm0, [eax + ecx]
  2379. movq mm1, [eax + ecx + 8]
  2380. movq [edx + ecx], mm0
  2381. movq [edx + ecx + 8], mm1
  2382. {$endif}
  2383. {Are there another 16 bytes to move?}
  2384. add ecx, 16
  2385. js @MMXMoveLoop
  2386. @MMXMoveLast12:
  2387. {Do the last 12 bytes}
  2388. {$ifdef Delphi4or5}
  2389. {Delphi 5 compatibility}
  2390. db $0f, $6f, $04, $01
  2391. {$else}
  2392. movq mm0, [eax + ecx]
  2393. {$endif}
  2394. mov eax, [eax + ecx + 8]
  2395. {$ifdef Delphi4or5}
  2396. {Delphi 5 compatibility}
  2397. db $0f, $7f, $04, $11
  2398. {$else}
  2399. movq [edx + ecx], mm0
  2400. {$endif}
  2401. mov [edx + ecx + 8], eax
  2402. {Exit MMX state}
  2403. {$ifdef Delphi4or5}
  2404. {Delphi 5 compatibility}
  2405. db $0f, $77
  2406. {$else}
  2407. emms
  2408. {$endif}
  2409. {$ifndef ForceMMX}
  2410. ret
  2411. {$endif}
  2412. {$endif}
  2413. {FPU code is only used if MMX is not forced}
  2414. {$ifndef ForceMMX}
  2415. @FPUMove:
  2416. neg ecx
  2417. jns @FPUMoveLast12
  2418. @FPUMoveLoop:
  2419. {Move a 16 byte block}
  2420. fild qword ptr [eax + ecx]
  2421. fild qword ptr [eax + ecx + 8]
  2422. fistp qword ptr [edx + ecx + 8]
  2423. fistp qword ptr [edx + ecx]
  2424. {Are there another 16 bytes to move?}
  2425. add ecx, 16
  2426. js @FPUMoveLoop
  2427. @FPUMoveLast12:
  2428. {Do the last 12 bytes}
  2429. fild qword ptr [eax + ecx]
  2430. fistp qword ptr [edx + ecx]
  2431. mov eax, [eax + ecx + 8]
  2432. mov [edx + ecx + 8], eax
  2433. {$endif}
  2434. {$else}
  2435. .noframe
  2436. {Make the counter negative based: The last 8 bytes are moved separately}
  2437. sub r8, 8
  2438. add rcx, r8
  2439. add rdx, r8
  2440. neg r8
  2441. jns @MoveLast12
  2442. @MoveLoop:
  2443. {Move a 16 byte block}
  2444. movdqa xmm0, [rcx + r8]
  2445. movdqa [rdx + r8], xmm0
  2446. {Are there another 16 bytes to move?}
  2447. add r8, 16
  2448. js @MoveLoop
  2449. @MoveLast12:
  2450. {Do the last 8 bytes}
  2451. mov r9, [rcx + r8]
  2452. mov [rdx + r8], r9
  2453. {$endif}
  2454. end;
  2455. {Variable size move procedure: Rounds ACount up to the next multiple of 8 less
  2456. SizeOf(Pointer). Important note: Always moves at least 8 - SizeOf(Pointer)
  2457. bytes (the minimum small block size with 8 byte alignment), irrespective of
  2458. ACount.}
  2459. procedure MoveX8LP(const ASource; var ADest; ACount: NativeInt);
  2460. asm
  2461. {$ifdef 32Bit}
  2462. {Make the counter negative based: The last 4 bytes are moved separately}
  2463. sub ecx, 4
  2464. {4 bytes or less? -> Use the Move4 routine.}
  2465. jle @FourBytesOrLess
  2466. add eax, ecx
  2467. add edx, ecx
  2468. neg ecx
  2469. {$ifdef EnableMMX}
  2470. {$ifndef ForceMMX}
  2471. cmp UseMMX, True
  2472. jne @FPUMoveLoop
  2473. {$endif}
  2474. @MMXMoveLoop:
  2475. {Move an 8 byte block}
  2476. {$ifdef Delphi4or5}
  2477. {Delphi 5 compatibility}
  2478. db $0f, $6f, $04, $01
  2479. db $0f, $7f, $04, $11
  2480. {$else}
  2481. movq mm0, [eax + ecx]
  2482. movq [edx + ecx], mm0
  2483. {$endif}
  2484. {Are there another 8 bytes to move?}
  2485. add ecx, 8
  2486. js @MMXMoveLoop
  2487. {Exit MMX state}
  2488. {$ifdef Delphi4or5}
  2489. {Delphi 5 compatibility}
  2490. db $0f, $77
  2491. {$else}
  2492. emms
  2493. {$endif}
  2494. {Do the last 4 bytes}
  2495. mov eax, [eax + ecx]
  2496. mov [edx + ecx], eax
  2497. ret
  2498. {$endif}
  2499. {FPU code is only used if MMX is not forced}
  2500. {$ifndef ForceMMX}
  2501. @FPUMoveLoop:
  2502. {Move an 8 byte block}
  2503. fild qword ptr [eax + ecx]
  2504. fistp qword ptr [edx + ecx]
  2505. {Are there another 8 bytes to move?}
  2506. add ecx, 8
  2507. js @FPUMoveLoop
  2508. {Do the last 4 bytes}
  2509. mov eax, [eax + ecx]
  2510. mov [edx + ecx], eax
  2511. ret
  2512. {$endif}
  2513. @FourBytesOrLess:
  2514. {Four or less bytes to move}
  2515. mov eax, [eax]
  2516. mov [edx], eax
  2517. {$else}
  2518. .noframe
  2519. {Make the counter negative based}
  2520. add rcx, r8
  2521. add rdx, r8
  2522. neg r8
  2523. @MoveLoop:
  2524. {Move an 8 byte block}
  2525. mov r9, [rcx + r8]
  2526. mov [rdx + r8], r9
  2527. {Are there another 8 bytes to move?}
  2528. add r8, 8
  2529. js @MoveLoop
  2530. {$endif}
  2531. end;
  2532. {----------------Windows Emulation Functions for Kylix Support-----------------}
  2533. {$ifdef Linux}
  2534. const
  2535. {Messagebox constants}
  2536. MB_OK = 0;
  2537. MB_ICONERROR = $10;
  2538. MB_TASKMODAL = $2000;
  2539. MB_DEFAULT_DESKTOP_ONLY = $20000;
  2540. {Virtual memory constants}
  2541. MEM_COMMIT = $1000;
  2542. MEM_RELEASE = $8000;
  2543. MEM_TOP_DOWN = $100000;
  2544. PAGE_READWRITE = 4;
  2545. procedure MessageBoxA(hWnd: Cardinal; AMessageText, AMessageTitle: PAnsiChar; uType: Cardinal); stdcall;
  2546. begin
  2547. writeln(AMessageText);
  2548. end;
  2549. function VirtualAlloc(lpvAddress: Pointer; dwSize, flAllocationType, flProtect: Cardinal): Pointer; stdcall;
  2550. begin
  2551. Result := valloc(dwSize);
  2552. end;
  2553. function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: Cardinal): LongBool; stdcall;
  2554. begin
  2555. free(lpAddress);
  2556. Result := True;
  2557. end;
  2558. {$ifndef NeverSleepOnThreadContention}
  2559. procedure Sleep(dwMilliseconds: Cardinal); stdcall;
  2560. begin
  2561. {Convert to microseconds (more or less)}
  2562. usleep(dwMilliseconds shl 10);
  2563. end;
  2564. {$endif}
  2565. {$endif}
  2566. {-----------------Debugging Support Functions and Procedures------------------}
  2567. {$ifdef FullDebugMode}
  2568. {Returns the current thread ID}
  2569. function GetThreadID: Cardinal;
  2570. {$ifdef 32Bit}
  2571. asm
  2572. mov eax, FS:[$24]
  2573. end;
  2574. {$else}
  2575. begin
  2576. Result := GetCurrentThreadId;
  2577. end;
  2578. {$endif}
  2579. {Fills a block of memory with the given dword (32-bit) or qword (64-bit).
  2580. Always fills a multiple of SizeOf(Pointer) bytes}
  2581. procedure DebugFillMem(var AAddress; AByteCount: NativeInt; AFillValue: NativeUInt);
  2582. asm
  2583. {$ifdef 32Bit}
  2584. {On Entry:
  2585. eax = AAddress
  2586. edx = AByteCount
  2587. ecx = AFillValue}
  2588. add eax, edx
  2589. neg edx
  2590. jns @Done
  2591. @FillLoop:
  2592. mov [eax + edx], ecx
  2593. add edx, 4
  2594. js @FillLoop
  2595. @Done:
  2596. {$else}
  2597. {On Entry:
  2598. rcx = AAddress
  2599. rdx = AByteCount
  2600. r8 = AFillValue}
  2601. add rcx, rdx
  2602. neg rdx
  2603. jns @Done
  2604. @FillLoop:
  2605. mov [rcx + rdx], r8
  2606. add rdx, 8
  2607. js @FillLoop
  2608. @Done:
  2609. {$endif}
  2610. end;
  2611. {$ifndef LoadDebugDLLDynamically}
  2612. {The stack trace procedure. The stack trace module is external since it may
  2613. raise handled access violations that result in the creation of exception
  2614. objects and the stack trace code is not re-entrant.}
  2615. procedure GetStackTrace(AReturnAddresses: PNativeUInt;
  2616. AMaxDepth, ASkipFrames: Cardinal); external FullDebugModeLibraryName
  2617. name {$ifdef RawStackTraces}'GetRawStackTrace'{$else}'GetFrameBasedStackTrace'{$endif};
  2618. {The exported procedure in the FastMM_FullDebugMode.dll library used to convert
  2619. the return addresses of a stack trace to a text string.}
  2620. function LogStackTrace(AReturnAddresses: PNativeUInt;
  2621. AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar; external FullDebugModeLibraryName
  2622. name 'LogStackTrace';
  2623. {$else}
  2624. {Default no-op stack trace and logging handlers}
  2625. procedure NoOpGetStackTrace(AReturnAddresses: PNativeUInt;
  2626. AMaxDepth, ASkipFrames: Cardinal);
  2627. begin
  2628. DebugFillMem(AReturnAddresses^, AMaxDepth * SizeOf(Pointer), 0);
  2629. end;
  2630. function NoOpLogStackTrace(AReturnAddresses: PNativeUInt;
  2631. AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
  2632. begin
  2633. Result := ABuffer;
  2634. end;
  2635. var
  2636. {Handle to the FullDebugMode DLL}
  2637. FullDebugModeDLL: HMODULE;
  2638. GetStackTrace: procedure (AReturnAddresses: PNativeUInt;
  2639. AMaxDepth, ASkipFrames: Cardinal) = NoOpGetStackTrace;
  2640. LogStackTrace: function (AReturnAddresses: PNativeUInt;
  2641. AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar = NoOpLogStackTrace;
  2642. {$endif}
  2643. {$endif}
  2644. {$ifndef Linux}
  2645. function DelphiIsRunning: Boolean;
  2646. begin
  2647. Result := FindWindowA('TAppBuilder', nil) <> 0;
  2648. end;
  2649. {$endif}
  2650. {Converts an unsigned integer to string at the buffer location, returning the
  2651. new buffer position. Note: The 32-bit asm version only supports numbers up to
  2652. 2^31 - 1.}
  2653. function NativeUIntToStrBuf(ANum: NativeUInt; APBuffer: PAnsiChar): PAnsiChar;
  2654. {$ifndef Use32BitAsm}
  2655. const
  2656. MaxDigits = 20;
  2657. var
  2658. LDigitBuffer: array[0..MaxDigits - 1] of AnsiChar;
  2659. LCount: Cardinal;
  2660. LDigit: NativeUInt;
  2661. begin
  2662. {Generate the digits in the local buffer}
  2663. LCount := 0;
  2664. repeat
  2665. LDigit := ANum;
  2666. ANum := ANum div 10;
  2667. LDigit := LDigit - ANum * 10;
  2668. Inc(LCount);
  2669. LDigitBuffer[MaxDigits - LCount] := AnsiChar(Ord('0') + LDigit);
  2670. until ANum = 0;
  2671. {Copy the digits to the output buffer and advance it}
  2672. System.Move(LDigitBuffer[MaxDigits - LCount], APBuffer^, LCount);
  2673. Result := APBuffer + LCount;
  2674. end;
  2675. {$else}
  2676. asm
  2677. {On entry: eax = ANum, edx = ABuffer}
  2678. push edi
  2679. mov edi, edx //Pointer to the first character in edi
  2680. {Calculate leading digit: divide the number by 1e9}
  2681. add eax, 1 //Increment the number
  2682. mov edx, $89705F41 //1e9 reciprocal
  2683. mul edx //Multplying with reciprocal
  2684. shr eax, 30 //Save fraction bits
  2685. mov ecx, edx //First digit in bits <31:29>
  2686. and edx, $1FFFFFFF //Filter fraction part edx<28:0>
  2687. shr ecx, 29 //Get leading digit into accumulator
  2688. lea edx, [edx + 4 * edx] //Calculate ...
  2689. add edx, eax //... 5*fraction
  2690. mov eax, ecx //Copy leading digit
  2691. or eax, '0' //Convert digit to ASCII
  2692. mov [edi], al //Store digit out to memory
  2693. {Calculate digit #2}
  2694. mov eax, edx //Point format such that 1.0 = 2^28
  2695. cmp ecx, 1 //Any non-zero digit yet ?
  2696. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2697. shr eax, 28 //Next digit
  2698. and edx, $0fffffff //Fraction part edx<27:0>
  2699. or ecx, eax //Accumulate next digit
  2700. or eax, '0' //Convert digit to ASCII
  2701. mov [edi], al //Store digit out to memory
  2702. {Calculate digit #3}
  2703. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:27>
  2704. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<26:0>
  2705. cmp ecx, 1 //Any non-zero digit yet ?
  2706. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2707. shr eax, 27 //Next digit
  2708. and edx, $07ffffff //Fraction part
  2709. or ecx, eax //Accumulate next digit
  2710. or eax, '0' //Convert digit to ASCII
  2711. mov [edi], al //Store digit out to memory
  2712. {Calculate digit #4}
  2713. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:26>
  2714. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<25:0>
  2715. cmp ecx, 1 //Any non-zero digit yet ?
  2716. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2717. shr eax, 26 //Next digit
  2718. and edx, $03ffffff //Fraction part
  2719. or ecx, eax //Accumulate next digit
  2720. or eax, '0' //Convert digit to ASCII
  2721. mov [edi], al //Store digit out to memory
  2722. {Calculate digit #5}
  2723. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:25>
  2724. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<24:0>
  2725. cmp ecx, 1 //Any non-zero digit yet ?
  2726. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2727. shr eax, 25 //Next digit
  2728. and edx, $01ffffff //Fraction part
  2729. or ecx, eax //Accumulate next digit
  2730. or eax, '0' //Convert digit to ASCII
  2731. mov [edi], al //Store digit out to memory
  2732. {Calculate digit #6}
  2733. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:24>
  2734. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<23:0>
  2735. cmp ecx, 1 //Any non-zero digit yet ?
  2736. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2737. shr eax, 24 //Next digit
  2738. and edx, $00ffffff //Fraction part
  2739. or ecx, eax //Accumulate next digit
  2740. or eax, '0' //Convert digit to ASCII
  2741. mov [edi], al //Store digit out to memory
  2742. {Calculate digit #7}
  2743. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:23>
  2744. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<31:23>
  2745. cmp ecx, 1 //Any non-zero digit yet ?
  2746. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2747. shr eax, 23 //Next digit
  2748. and edx, $007fffff //Fraction part
  2749. or ecx, eax //Accumulate next digit
  2750. or eax, '0' //Convert digit to ASCII
  2751. mov [edi], al //Store digit out to memory
  2752. {Calculate digit #8}
  2753. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:22>
  2754. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<22:0>
  2755. cmp ecx, 1 //Any non-zero digit yet ?
  2756. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2757. shr eax, 22 //Next digit
  2758. and edx, $003fffff //Fraction part
  2759. or ecx, eax //Accumulate next digit
  2760. or eax, '0' //Convert digit to ASCII
  2761. mov [edi], al //Store digit out to memory
  2762. {Calculate digit #9}
  2763. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:21>
  2764. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<21:0>
  2765. cmp ecx, 1 //Any non-zero digit yet ?
  2766. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2767. shr eax, 21 //Next digit
  2768. and edx, $001fffff //Fraction part
  2769. or ecx, eax //Accumulate next digit
  2770. or eax, '0' //Convert digit to ASCII
  2771. mov [edi], al //Store digit out to memory
  2772. {Calculate digit #10}
  2773. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:20>
  2774. cmp ecx, 1 //Any-non-zero digit yet ?
  2775. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2776. shr eax, 20 //Next digit
  2777. or eax, '0' //Convert digit to ASCII
  2778. mov [edi], al //Store last digit and end marker out to memory
  2779. {Return a pointer to the next character}
  2780. lea eax, [edi + 1]
  2781. {Restore edi}
  2782. pop edi
  2783. end;
  2784. {$endif}
  2785. {Converts an unsigned integer to a hexadecimal string at the buffer location,
  2786. returning the new buffer position.}
  2787. function NativeUIntToHexBuf(ANum: NativeUInt; APBuffer: PAnsiChar): PAnsiChar;
  2788. {$ifndef Use32BitAsm}
  2789. const
  2790. MaxDigits = 16;
  2791. var
  2792. LDigitBuffer: array[0..MaxDigits - 1] of AnsiChar;
  2793. LCount: Cardinal;
  2794. LDigit: NativeUInt;
  2795. begin
  2796. {Generate the digits in the local buffer}
  2797. LCount := 0;
  2798. repeat
  2799. LDigit := ANum;
  2800. ANum := ANum div 16;
  2801. LDigit := LDigit - ANum * 16;
  2802. Inc(LCount);
  2803. LDigitBuffer[MaxDigits - LCount] := HexTable[LDigit];
  2804. until ANum = 0;
  2805. {Copy the digits to the output buffer and advance it}
  2806. System.Move(LDigitBuffer[MaxDigits - LCount], APBuffer^, LCount);
  2807. Result := APBuffer + LCount;
  2808. end;
  2809. {$else}
  2810. asm
  2811. {On entry:
  2812. eax = ANum
  2813. edx = ABuffer}
  2814. push ebx
  2815. push edi
  2816. {Save ANum in ebx}
  2817. mov ebx, eax
  2818. {Get a pointer to the first character in edi}
  2819. mov edi, edx
  2820. {Get the number in ecx as well}
  2821. mov ecx, eax
  2822. {Keep the low nibbles in ebx and the high nibbles in ecx}
  2823. and ebx, $0f0f0f0f
  2824. and ecx, $f0f0f0f0
  2825. {Swap the bytes into the right order}
  2826. ror ebx, 16
  2827. ror ecx, 20
  2828. {Get nibble 7}
  2829. movzx eax, ch
  2830. mov dl, ch
  2831. mov al, byte ptr HexTable[eax]
  2832. mov [edi], al
  2833. cmp dl, 1
  2834. sbb edi, -1
  2835. {Get nibble 6}
  2836. movzx eax, bh
  2837. or dl, bh
  2838. mov al, byte ptr HexTable[eax]
  2839. mov [edi], al
  2840. cmp dl, 1
  2841. sbb edi, -1
  2842. {Get nibble 5}
  2843. movzx eax, cl
  2844. or dl, cl
  2845. mov al, byte ptr HexTable[eax]
  2846. mov [edi], al
  2847. cmp dl, 1
  2848. sbb edi, -1
  2849. {Get nibble 4}
  2850. movzx eax, bl
  2851. or dl, bl
  2852. mov al, byte ptr HexTable[eax]
  2853. mov [edi], al
  2854. cmp dl, 1
  2855. sbb edi, -1
  2856. {Rotate ecx and ebx so we get access to the rest}
  2857. shr ebx, 16
  2858. shr ecx, 16
  2859. {Get nibble 3}
  2860. movzx eax, ch
  2861. or dl, ch
  2862. mov al, byte ptr HexTable[eax]
  2863. mov [edi], al
  2864. cmp dl, 1
  2865. sbb edi, -1
  2866. {Get nibble 2}
  2867. movzx eax, bh
  2868. or dl, bh
  2869. mov al, byte ptr HexTable[eax]
  2870. mov [edi], al
  2871. cmp dl, 1
  2872. sbb edi, -1
  2873. {Get nibble 1}
  2874. movzx eax, cl
  2875. or dl, cl
  2876. mov al, byte ptr HexTable[eax]
  2877. mov [edi], al
  2878. cmp dl, 1
  2879. sbb edi, -1
  2880. {Get nibble 0}
  2881. movzx eax, bl
  2882. mov al, byte ptr HexTable[eax]
  2883. mov [edi], al
  2884. {Return a pointer to the end of the string}
  2885. lea eax, [edi + 1]
  2886. {Restore registers}
  2887. pop edi
  2888. pop ebx
  2889. end;
  2890. {$endif}
  2891. {Appends the source text to the destination and returns the new destination
  2892. position}
  2893. function AppendStringToBuffer(const ASource, ADestination: PAnsiChar; ACount: Cardinal): PAnsiChar;
  2894. begin
  2895. System.Move(ASource^, ADestination^, ACount);
  2896. Result := Pointer(PByte(ADestination) + ACount);
  2897. end;
  2898. {Appends the name of the class to the destination buffer and returns the new
  2899. destination position}
  2900. function AppendClassNameToBuffer(AClass: TClass; ADestination: PAnsiChar): PAnsiChar;
  2901. var
  2902. LPClassName: PShortString;
  2903. begin
  2904. {Get a pointer to the class name}
  2905. if AClass <> nil then
  2906. begin
  2907. LPClassName := PShortString(PPointer(PByte(AClass) + vmtClassName)^);
  2908. {Append the class name}
  2909. Result := AppendStringToBuffer(@LPClassName^[1], ADestination, Length(LPClassName^));
  2910. end
  2911. else
  2912. begin
  2913. Result := AppendStringToBuffer(UnknownClassNameMsg, ADestination, Length(UnknownClassNameMsg));
  2914. end;
  2915. end;
  2916. {Shows a message box if the program is not showing one already.}
  2917. procedure ShowMessageBox(AText, ACaption: PAnsiChar);
  2918. begin
  2919. if (not ShowingMessageBox) and (not SuppressMessageBoxes) then
  2920. begin
  2921. ShowingMessageBox := True;
  2922. MessageBoxA(0, AText, ACaption,
  2923. MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_DEFAULT_DESKTOP_ONLY);
  2924. ShowingMessageBox := False;
  2925. end;
  2926. end;
  2927. {Returns the class for a memory block. Returns nil if it is not a valid class}
  2928. function DetectClassInstance(APointer: Pointer): TClass;
  2929. {$ifndef Linux}
  2930. var
  2931. LMemInfo: TMemoryBasicInformation;
  2932. {Checks whether the given address is a valid address for a VMT entry.}
  2933. function IsValidVMTAddress(APAddress: Pointer): Boolean;
  2934. begin
  2935. {Do some basic pointer checks: Must be dword aligned and beyond 64K}
  2936. if (UIntPtr(APAddress) > 65535)
  2937. and (UIntPtr(APAddress) and 3 = 0) then
  2938. begin
  2939. {Do we need to recheck the virtual memory?}
  2940. if (UIntPtr(LMemInfo.BaseAddress) > UIntPtr(APAddress))
  2941. or ((UIntPtr(LMemInfo.BaseAddress) + LMemInfo.RegionSize) < (UIntPtr(APAddress) + 4)) then
  2942. begin
  2943. {Get the VM status for the pointer}
  2944. LMemInfo.RegionSize := 0;
  2945. VirtualQuery(APAddress, LMemInfo, SizeOf(LMemInfo));
  2946. end;
  2947. {Check the readability of the memory address}
  2948. Result := (LMemInfo.RegionSize >= 4)
  2949. and (LMemInfo.State = MEM_COMMIT)
  2950. and (LMemInfo.Protect and (PAGE_READONLY or PAGE_READWRITE or PAGE_EXECUTE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY) <> 0)
  2951. and (LMemInfo.Protect and PAGE_GUARD = 0);
  2952. end
  2953. else
  2954. Result := False;
  2955. end;
  2956. {Returns true if AClassPointer points to a class VMT}
  2957. function InternalIsValidClass(AClassPointer: Pointer; ADepth: Integer = 0): Boolean;
  2958. var
  2959. LParentClassSelfPointer: PPointer;
  2960. begin
  2961. {Check that the self pointer as well as parent class self pointer addresses
  2962. are valid}
  2963. if (ADepth < 1000)
  2964. and IsValidVMTAddress(Pointer(PByte(AClassPointer) + vmtSelfPtr))
  2965. and IsValidVMTAddress(Pointer(PByte(AClassPointer) + vmtParent)) then
  2966. begin
  2967. {Get a pointer to the parent class' self pointer}
  2968. LParentClassSelfPointer := PPointer(PByte(AClassPointer) + vmtParent)^;
  2969. {Check that the self pointer as well as the parent class is valid}
  2970. Result := (PPointer(PByte(AClassPointer) + vmtSelfPtr)^ = AClassPointer)
  2971. and ((LParentClassSelfPointer = nil)
  2972. or (IsValidVMTAddress(LParentClassSelfPointer)
  2973. and InternalIsValidClass(LParentClassSelfPointer^, ADepth + 1)));
  2974. end
  2975. else
  2976. Result := False;
  2977. end;
  2978. begin
  2979. {Get the class pointer from the (suspected) object}
  2980. Result := TClass(PPointer(APointer)^);
  2981. {No VM info yet}
  2982. LMemInfo.RegionSize := 0;
  2983. {Check the block}
  2984. if (not InternalIsValidClass(Pointer(Result), 0))
  2985. {$ifdef FullDebugMode}
  2986. or (Result = @FreedObjectVMT.VMTMethods[0])
  2987. {$endif}
  2988. then
  2989. Result := nil;
  2990. end;
  2991. {$else}
  2992. begin
  2993. {Not currently supported under Linux}
  2994. Result := nil;
  2995. end;
  2996. {$endif}
  2997. {Gets the available size inside a block}
  2998. function GetAvailableSpaceInBlock(APointer: Pointer): NativeUInt;
  2999. var
  3000. LBlockHeader: NativeUInt;
  3001. LPSmallBlockPool: PSmallBlockPoolHeader;
  3002. begin
  3003. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  3004. if LBlockHeader and (IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
  3005. begin
  3006. LPSmallBlockPool := PSmallBlockPoolHeader(LBlockHeader and DropSmallFlagsMask);
  3007. Result := LPSmallBlockPool.BlockType.BlockSize - BlockHeaderSize;
  3008. end
  3009. else
  3010. begin
  3011. Result := (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
  3012. if (LBlockHeader and IsMediumBlockFlag) = 0 then
  3013. Dec(Result, LargeBlockHeaderSize);
  3014. end;
  3015. end;
  3016. {-----------------Small Block Management------------------}
  3017. {Locks all small block types}
  3018. procedure LockAllSmallBlockTypes;
  3019. var
  3020. LInd: Cardinal;
  3021. begin
  3022. {Lock the medium blocks}
  3023. {$ifndef AssumeMultiThreaded}
  3024. if IsMultiThread then
  3025. {$endif}
  3026. begin
  3027. for LInd := 0 to NumSmallBlockTypes - 1 do
  3028. begin
  3029. while LockCmpxchg(0, 1, @SmallBlockTypes[LInd].BlockTypeLocked) <> 0 do
  3030. begin
  3031. {$ifdef NeverSleepOnThreadContention}
  3032. {$ifdef UseSwitchToThread}
  3033. SwitchToThread;
  3034. {$endif}
  3035. {$else}
  3036. Sleep(InitialSleepTime);
  3037. if LockCmpxchg(0, 1, @SmallBlockTypes[LInd].BlockTypeLocked) = 0 then
  3038. Break;
  3039. Sleep(AdditionalSleepTime);
  3040. {$endif}
  3041. end;
  3042. end;
  3043. end;
  3044. end;
  3045. {Gets the first and last block pointer for a small block pool}
  3046. procedure GetFirstAndLastSmallBlockInPool(APSmallBlockPool: PSmallBlockPoolHeader;
  3047. var AFirstPtr, ALastPtr: Pointer);
  3048. var
  3049. LBlockSize: NativeUInt;
  3050. begin
  3051. {Get the pointer to the first block}
  3052. AFirstPtr := Pointer(PByte(APSmallBlockPool) + SmallBlockPoolHeaderSize);
  3053. {Get a pointer to the last block}
  3054. if (APSmallBlockPool.BlockType.CurrentSequentialFeedPool <> APSmallBlockPool)
  3055. or (UIntPtr(APSmallBlockPool.BlockType.NextSequentialFeedBlockAddress) > UIntPtr(APSmallBlockPool.BlockType.MaxSequentialFeedBlockAddress)) then
  3056. begin
  3057. {Not the sequential feed - point to the end of the block}
  3058. LBlockSize := PNativeUInt(PByte(APSmallBlockPool) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  3059. ALastPtr := Pointer(PByte(APSmallBlockPool) + LBlockSize - APSmallBlockPool.BlockType.BlockSize);
  3060. end
  3061. else
  3062. begin
  3063. {The sequential feed pool - point to before the next sequential feed block}
  3064. ALastPtr := Pointer(PByte(APSmallBlockPool.BlockType.NextSequentialFeedBlockAddress) - 1);
  3065. end;
  3066. end;
  3067. {-----------------Medium Block Management------------------}
  3068. {Advances to the next medium block. Returns nil if the end of the medium block
  3069. pool has been reached}
  3070. function NextMediumBlock(APMediumBlock: Pointer): Pointer;
  3071. var
  3072. LBlockSize: NativeUInt;
  3073. begin
  3074. {Get the size of this block}
  3075. LBlockSize := PNativeUInt(PByte(APMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  3076. {Advance the pointer}
  3077. Result := Pointer(PByte(APMediumBlock) + LBlockSize);
  3078. {Is the next block the end of medium pool marker?}
  3079. LBlockSize := PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  3080. if LBlockSize = 0 then
  3081. Result := nil;
  3082. end;
  3083. {Gets the first medium block in the medium block pool}
  3084. function GetFirstMediumBlockInPool(APMediumBlockPoolHeader: PMediumBlockPoolHeader): Pointer;
  3085. begin
  3086. if (MediumSequentialFeedBytesLeft = 0)
  3087. or (UIntPtr(LastSequentiallyFedMediumBlock) < UIntPtr(APMediumBlockPoolHeader))
  3088. or (UIntPtr(LastSequentiallyFedMediumBlock) > UIntPtr(APMediumBlockPoolHeader) + MediumBlockPoolSize) then
  3089. begin
  3090. Result := Pointer(PByte(APMediumBlockPoolHeader) + MediumBlockPoolHeaderSize);
  3091. end
  3092. else
  3093. begin
  3094. {Is the sequential feed pool empty?}
  3095. if MediumSequentialFeedBytesLeft <> MediumBlockPoolSize - MediumBlockPoolHeaderSize then
  3096. Result := LastSequentiallyFedMediumBlock
  3097. else
  3098. Result := nil;
  3099. end;
  3100. end;
  3101. {Locks the medium blocks. Note that the 32-bit asm version is assumed to
  3102. preserve all registers except eax.}
  3103. {$ifndef Use32BitAsm}
  3104. procedure LockMediumBlocks;
  3105. begin
  3106. {Lock the medium blocks}
  3107. {$ifndef AssumeMultiThreaded}
  3108. if IsMultiThread then
  3109. {$endif}
  3110. begin
  3111. while LockCmpxchg(0, 1, @MediumBlocksLocked) <> 0 do
  3112. begin
  3113. {$ifdef NeverSleepOnThreadContention}
  3114. {$ifdef UseSwitchToThread}
  3115. SwitchToThread;
  3116. {$endif}
  3117. {$else}
  3118. Sleep(InitialSleepTime);
  3119. if LockCmpxchg(0, 1, @MediumBlocksLocked) = 0 then
  3120. Break;
  3121. Sleep(AdditionalSleepTime);
  3122. {$endif}
  3123. end;
  3124. end;
  3125. end;
  3126. {$else}
  3127. procedure LockMediumBlocks;
  3128. asm
  3129. {Note: This routine is assumed to preserve all registers except eax}
  3130. @MediumBlockLockLoop:
  3131. mov eax, $100
  3132. {Attempt to lock the medium blocks}
  3133. lock cmpxchg MediumBlocksLocked, ah
  3134. je @Done
  3135. {$ifdef NeverSleepOnThreadContention}
  3136. {Pause instruction (improves performance on P4)}
  3137. rep nop
  3138. {$ifdef UseSwitchToThread}
  3139. push ecx
  3140. push edx
  3141. call SwitchToThread
  3142. pop edx
  3143. pop ecx
  3144. {$endif}
  3145. {Try again}
  3146. jmp @MediumBlockLockLoop
  3147. {$else}
  3148. {Couldn't lock the medium blocks - sleep and try again}
  3149. push ecx
  3150. push edx
  3151. push InitialSleepTime
  3152. call Sleep
  3153. pop edx
  3154. pop ecx
  3155. {Try again}
  3156. mov eax, $100
  3157. {Attempt to grab the block type}
  3158. lock cmpxchg MediumBlocksLocked, ah
  3159. je @Done
  3160. {Couldn't lock the medium blocks - sleep and try again}
  3161. push ecx
  3162. push edx
  3163. push AdditionalSleepTime
  3164. call Sleep
  3165. pop edx
  3166. pop ecx
  3167. {Try again}
  3168. jmp @MediumBlockLockLoop
  3169. {$endif}
  3170. @Done:
  3171. end;
  3172. {$endif}
  3173. {Removes a medium block from the circular linked list of free blocks.
  3174. Does not change any header flags. Medium blocks should be locked
  3175. before calling this procedure.}
  3176. procedure RemoveMediumFreeBlock(APMediumFreeBlock: PMediumFreeBlock);
  3177. {$ifndef ASMVersion}
  3178. var
  3179. LPreviousFreeBlock, LNextFreeBlock: PMediumFreeBlock;
  3180. LBinNumber, LBinGroupNumber: Cardinal;
  3181. begin
  3182. {Get the current previous and next blocks}
  3183. LNextFreeBlock := APMediumFreeBlock.NextFreeBlock;
  3184. LPreviousFreeBlock := APMediumFreeBlock.PreviousFreeBlock;
  3185. {Remove this block from the linked list}
  3186. LPreviousFreeBlock.NextFreeBlock := LNextFreeBlock;
  3187. LNextFreeBlock.PreviousFreeBlock := LPreviousFreeBlock;
  3188. {Is this bin now empty? If the previous and next free block pointers are
  3189. equal, they must point to the bin.}
  3190. if LPreviousFreeBlock = LNextFreeBlock then
  3191. begin
  3192. {Get the bin number for this block size}
  3193. LBinNumber := (UIntPtr(LNextFreeBlock) - UIntPtr(@MediumBlockBins)) div SizeOf(TMediumFreeBlock);
  3194. LBinGroupNumber := LBinNumber div 32;
  3195. {Flag this bin as empty}
  3196. MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
  3197. and (not (1 shl (LBinNumber and 31)));
  3198. {Is the group now entirely empty?}
  3199. if MediumBlockBinBitmaps[LBinGroupNumber] = 0 then
  3200. begin
  3201. {Flag this group as empty}
  3202. MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
  3203. and (not (1 shl LBinGroupNumber));
  3204. end;
  3205. end;
  3206. end;
  3207. {$else}
  3208. {$ifdef 32Bit}
  3209. asm
  3210. {On entry: eax = APMediumFreeBlock}
  3211. {Get the current previous and next blocks}
  3212. mov ecx, TMediumFreeBlock[eax].NextFreeBlock
  3213. mov edx, TMediumFreeBlock[eax].PreviousFreeBlock
  3214. {Is this bin now empty? If the previous and next free block pointers are
  3215. equal, they must point to the bin.}
  3216. cmp ecx, edx
  3217. {Remove this block from the linked list}
  3218. mov TMediumFreeBlock[ecx].PreviousFreeBlock, edx
  3219. mov TMediumFreeBlock[edx].NextFreeBlock, ecx
  3220. {Is this bin now empty? If the previous and next free block pointers are
  3221. equal, they must point to the bin.}
  3222. je @BinIsNowEmpty
  3223. @Done:
  3224. ret
  3225. {Align branch target}
  3226. nop
  3227. @BinIsNowEmpty:
  3228. {Get the bin number for this block size in ecx}
  3229. sub ecx, offset MediumBlockBins
  3230. mov edx, ecx
  3231. shr ecx, 3
  3232. {Get the group number in edx}
  3233. movzx edx, dh
  3234. {Flag this bin as empty}
  3235. mov eax, -2
  3236. rol eax, cl
  3237. and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
  3238. jnz @Done
  3239. {Flag this group as empty}
  3240. mov eax, -2
  3241. mov ecx, edx
  3242. rol eax, cl
  3243. and MediumBlockBinGroupBitmap, eax
  3244. end;
  3245. {$else}
  3246. asm
  3247. {On entry: rcx = APMediumFreeBlock}
  3248. mov rax, rcx
  3249. {Get the current previous and next blocks}
  3250. mov rcx, TMediumFreeBlock[rax].NextFreeBlock
  3251. mov rdx, TMediumFreeBlock[rax].PreviousFreeBlock
  3252. {Is this bin now empty? If the previous and next free block pointers are
  3253. equal, they must point to the bin.}
  3254. cmp rcx, rdx
  3255. {Remove this block from the linked list}
  3256. mov TMediumFreeBlock[rcx].PreviousFreeBlock, rdx
  3257. mov TMediumFreeBlock[rdx].NextFreeBlock, rcx
  3258. {Is this bin now empty? If the previous and next free block pointers are
  3259. equal, they must point to the bin.}
  3260. jne @Done
  3261. {Get the bin number for this block size in rcx}
  3262. lea r8, MediumBlockBins
  3263. sub rcx, r8
  3264. mov edx, ecx
  3265. shr ecx, 4
  3266. {Get the group number in edx}
  3267. shr edx, 9
  3268. {Flag this bin as empty}
  3269. mov eax, -2
  3270. rol eax, cl
  3271. lea r8, MediumBlockBinBitmaps
  3272. and dword ptr [r8 + rdx * 4], eax
  3273. jnz @Done
  3274. {Flag this group as empty}
  3275. mov eax, -2
  3276. mov ecx, edx
  3277. rol eax, cl
  3278. and MediumBlockBinGroupBitmap, eax
  3279. @Done:
  3280. end;
  3281. {$endif}
  3282. {$endif}
  3283. {Inserts a medium block into the appropriate medium block bin.}
  3284. procedure InsertMediumBlockIntoBin(APMediumFreeBlock: PMediumFreeBlock; AMediumBlockSize: Cardinal);
  3285. {$ifndef ASMVersion}
  3286. var
  3287. LBinNumber, LBinGroupNumber: Cardinal;
  3288. LPBin, LPFirstFreeBlock: PMediumFreeBlock;
  3289. begin
  3290. {Get the bin number for this block size. Get the bin that holds blocks of at
  3291. least this size.}
  3292. LBinNumber := (AMediumBlockSize - MinimumMediumBlockSize) div MediumBlockGranularity;
  3293. if LBinNumber >= MediumBlockBinCount then
  3294. LBinNumber := MediumBlockBinCount - 1;
  3295. {Get the bin}
  3296. LPBin := @MediumBlockBins[LBinNumber];
  3297. {Bins are LIFO, se we insert this block as the first free block in the bin}
  3298. LPFirstFreeBlock := LPBin.NextFreeBlock;
  3299. APMediumFreeBlock.PreviousFreeBlock := LPBin;
  3300. APMediumFreeBlock.NextFreeBlock := LPFirstFreeBlock;
  3301. LPFirstFreeBlock.PreviousFreeBlock := APMediumFreeBlock;
  3302. LPBin.NextFreeBlock := APMediumFreeBlock;
  3303. {Was this bin empty?}
  3304. if LPFirstFreeBlock = LPBin then
  3305. begin
  3306. {Get the group number}
  3307. LBinGroupNumber := LBinNumber div 32;
  3308. {Flag this bin as used}
  3309. MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
  3310. or (1 shl (LBinNumber and 31));
  3311. {Flag the group as used}
  3312. MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
  3313. or (1 shl LBinGroupNumber);
  3314. end;
  3315. end;
  3316. {$else}
  3317. {$ifdef 32Bit}
  3318. asm
  3319. {On entry: eax = APMediumFreeBlock, edx = AMediumBlockSize}
  3320. {Get the bin number for this block size. Get the bin that holds blocks of at
  3321. least this size.}
  3322. sub edx, MinimumMediumBlockSize
  3323. shr edx, 8
  3324. {Validate the bin number}
  3325. sub edx, MediumBlockBinCount - 1
  3326. sbb ecx, ecx
  3327. and edx, ecx
  3328. add edx, MediumBlockBinCount - 1
  3329. {Get the bin in ecx}
  3330. lea ecx, [MediumBlockBins + edx * 8]
  3331. {Bins are LIFO, se we insert this block as the first free block in the bin}
  3332. mov edx, TMediumFreeBlock[ecx].NextFreeBlock
  3333. {Was this bin empty?}
  3334. cmp edx, ecx
  3335. mov TMediumFreeBlock[eax].PreviousFreeBlock, ecx
  3336. mov TMediumFreeBlock[eax].NextFreeBlock, edx
  3337. mov TMediumFreeBlock[edx].PreviousFreeBlock, eax
  3338. mov TMediumFreeBlock[ecx].NextFreeBlock, eax
  3339. {Was this bin empty?}
  3340. je @BinWasEmpty
  3341. ret
  3342. {Align branch target}
  3343. nop
  3344. nop
  3345. @BinWasEmpty:
  3346. {Get the bin number in ecx}
  3347. sub ecx, offset MediumBlockBins
  3348. mov edx, ecx
  3349. shr ecx, 3
  3350. {Get the group number in edx}
  3351. movzx edx, dh
  3352. {Flag this bin as not empty}
  3353. mov eax, 1
  3354. shl eax, cl
  3355. or dword ptr [MediumBlockBinBitmaps + edx * 4], eax
  3356. {Flag the group as not empty}
  3357. mov eax, 1
  3358. mov ecx, edx
  3359. shl eax, cl
  3360. or MediumBlockBinGroupBitmap, eax
  3361. end;
  3362. {$else}
  3363. asm
  3364. {On entry: rax = APMediumFreeBlock, edx = AMediumBlockSize}
  3365. mov rax, rcx
  3366. {Get the bin number for this block size. Get the bin that holds blocks of at
  3367. least this size.}
  3368. sub edx, MinimumMediumBlockSize
  3369. shr edx, 8
  3370. {Validate the bin number}
  3371. sub edx, MediumBlockBinCount - 1
  3372. sbb ecx, ecx
  3373. and edx, ecx
  3374. add edx, MediumBlockBinCount - 1
  3375. mov r9, rdx
  3376. {Get the bin address in rcx}
  3377. lea rcx, MediumBlockBins
  3378. shl edx, 4
  3379. add rcx, rdx
  3380. {Bins are LIFO, se we insert this block as the first free block in the bin}
  3381. mov rdx, TMediumFreeBlock[rcx].NextFreeBlock
  3382. {Was this bin empty?}
  3383. cmp rdx, rcx
  3384. mov TMediumFreeBlock[rax].PreviousFreeBlock, rcx
  3385. mov TMediumFreeBlock[rax].NextFreeBlock, rdx
  3386. mov TMediumFreeBlock[rdx].PreviousFreeBlock, rax
  3387. mov TMediumFreeBlock[rcx].NextFreeBlock, rax
  3388. {Was this bin empty?}
  3389. jne @Done
  3390. {Get the bin number in ecx}
  3391. mov rcx, r9
  3392. {Get the group number in edx}
  3393. mov rdx, r9
  3394. shr edx, 5
  3395. {Flag this bin as not empty}
  3396. mov eax, 1
  3397. shl eax, cl
  3398. lea r8, MediumBlockBinBitmaps
  3399. or dword ptr [r8 + rdx * 4], eax
  3400. {Flag the group as not empty}
  3401. mov eax, 1
  3402. mov ecx, edx
  3403. shl eax, cl
  3404. or MediumBlockBinGroupBitmap, eax
  3405. @Done:
  3406. end;
  3407. {$endif}
  3408. {$endif}
  3409. {Bins what remains in the current sequential feed medium block pool. Medium
  3410. blocks must be locked.}
  3411. procedure BinMediumSequentialFeedRemainder;
  3412. {$ifndef Use32BitAsm}
  3413. var
  3414. LSequentialFeedFreeSize, LNextBlockSizeAndFlags: NativeUInt;
  3415. LPRemainderBlock, LNextMediumBlock: Pointer;
  3416. begin
  3417. LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
  3418. if LSequentialFeedFreeSize > 0 then
  3419. begin
  3420. {Get the block after the open space}
  3421. LNextMediumBlock := LastSequentiallyFedMediumBlock;
  3422. LNextBlockSizeAndFlags := PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^;
  3423. {Point to the remainder}
  3424. LPRemainderBlock := Pointer(PByte(LNextMediumBlock) - LSequentialFeedFreeSize);
  3425. {$ifndef FullDebugMode}
  3426. {Can the next block be combined with the remainder?}
  3427. if (LNextBlockSizeAndFlags and IsFreeBlockFlag) <> 0 then
  3428. begin
  3429. {Increase the size of this block}
  3430. Inc(LSequentialFeedFreeSize, LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask);
  3431. {Remove the next block as well}
  3432. if (LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask) >= MinimumMediumBlockSize then
  3433. RemoveMediumFreeBlock(LNextMediumBlock);
  3434. end
  3435. else
  3436. begin
  3437. {$endif}
  3438. {Set the "previous block is free" flag of the next block}
  3439. PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^ := LNextBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
  3440. {$ifndef FullDebugMode}
  3441. end;
  3442. {$endif}
  3443. {Store the size of the block as well as the flags}
  3444. PNativeUInt(PByte(LPRemainderBlock) - BlockHeaderSize)^ := LSequentialFeedFreeSize or IsMediumBlockFlag or IsFreeBlockFlag;
  3445. {Store the trailing size marker}
  3446. PNativeUInt(PByte(LPRemainderBlock) + LSequentialFeedFreeSize - BlockHeaderSize * 2)^ := LSequentialFeedFreeSize;
  3447. {$ifdef FullDebugMode}
  3448. {In full debug mode the sequential feed remainder will never be too small to
  3449. fit a full debug header.}
  3450. {Clear the user area of the block}
  3451. DebugFillMem(Pointer(PByte(LPRemainderBlock) + SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt))^,
  3452. LSequentialFeedFreeSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
  3453. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  3454. {We need to set a valid debug header and footer in the remainder}
  3455. PFullDebugBlockHeader(LPRemainderBlock).HeaderCheckSum := NativeUInt(LPRemainderBlock);
  3456. PNativeUInt(PByte(LPRemainderBlock) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(LPRemainderBlock);
  3457. {$endif}
  3458. {Bin this medium block}
  3459. if LSequentialFeedFreeSize >= MinimumMediumBlockSize then
  3460. InsertMediumBlockIntoBin(LPRemainderBlock, LSequentialFeedFreeSize);
  3461. end;
  3462. end;
  3463. {$else}
  3464. asm
  3465. cmp MediumSequentialFeedBytesLeft, 0
  3466. jne @MustBinMedium
  3467. {Nothing to bin}
  3468. ret
  3469. {Align branch target}
  3470. nop
  3471. nop
  3472. @MustBinMedium:
  3473. {Get a pointer to the last sequentially allocated medium block}
  3474. mov eax, LastSequentiallyFedMediumBlock
  3475. {Is the block that was last fed sequentially free?}
  3476. test byte ptr [eax - 4], IsFreeBlockFlag
  3477. jnz @LastBlockFedIsFree
  3478. {Set the "previous block is free" flag in the last block fed}
  3479. or dword ptr [eax - 4], PreviousMediumBlockIsFreeFlag
  3480. {Get the remainder in edx}
  3481. mov edx, MediumSequentialFeedBytesLeft
  3482. {Point eax to the start of the remainder}
  3483. sub eax, edx
  3484. @BinTheRemainder:
  3485. {Status: eax = start of remainder, edx = size of remainder}
  3486. {Store the size of the block as well as the flags}
  3487. lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  3488. mov [eax - 4], ecx
  3489. {Store the trailing size marker}
  3490. mov [eax + edx - 8], edx
  3491. {Bin this medium block}
  3492. cmp edx, MinimumMediumBlockSize
  3493. jnb InsertMediumBlockIntoBin
  3494. ret
  3495. {Align branch target}
  3496. nop
  3497. nop
  3498. @LastBlockFedIsFree:
  3499. {Drop the flags}
  3500. mov edx, DropMediumAndLargeFlagsMask
  3501. and edx, [eax - 4]
  3502. {Free the last block fed}
  3503. cmp edx, MinimumMediumBlockSize
  3504. jb @DontRemoveLastFed
  3505. {Last fed block is free - remove it from its size bin}
  3506. call RemoveMediumFreeBlock
  3507. {Re-read eax and edx}
  3508. mov eax, LastSequentiallyFedMediumBlock
  3509. mov edx, DropMediumAndLargeFlagsMask
  3510. and edx, [eax - 4]
  3511. @DontRemoveLastFed:
  3512. {Get the number of bytes left in ecx}
  3513. mov ecx, MediumSequentialFeedBytesLeft
  3514. {Point eax to the start of the remainder}
  3515. sub eax, ecx
  3516. {edx = total size of the remainder}
  3517. add edx, ecx
  3518. jmp @BinTheRemainder
  3519. @Done:
  3520. end;
  3521. {$endif}
  3522. {Allocates a new sequential feed medium block pool and immediately splits off a
  3523. block of the requested size. The block size must be a multiple of 16 and
  3524. medium blocks must be locked.}
  3525. function AllocNewSequentialFeedMediumPool(AFirstBlockSize: Cardinal): Pointer;
  3526. var
  3527. LOldFirstMediumBlockPool: PMediumBlockPoolHeader;
  3528. LNewPool: Pointer;
  3529. begin
  3530. {Bin the current sequential feed remainder}
  3531. BinMediumSequentialFeedRemainder;
  3532. {Allocate a new sequential feed block pool}
  3533. LNewPool := VirtualAlloc(nil, MediumBlockPoolSize,
  3534. MEM_COMMIT{$ifdef AlwaysAllocateTopDown} or MEM_TOP_DOWN{$endif}, PAGE_READWRITE);
  3535. if LNewPool <> nil then
  3536. begin
  3537. {Insert this block pool into the list of block pools}
  3538. LOldFirstMediumBlockPool := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  3539. PMediumBlockPoolHeader(LNewPool).PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  3540. MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := LNewPool;
  3541. PMediumBlockPoolHeader(LNewPool).NextMediumBlockPoolHeader := LOldFirstMediumBlockPool;
  3542. LOldFirstMediumBlockPool.PreviousMediumBlockPoolHeader := LNewPool;
  3543. {Store the sequential feed pool trailer}
  3544. PNativeUInt(PByte(LNewPool) + MediumBlockPoolSize - BlockHeaderSize)^ := IsMediumBlockFlag;
  3545. {Get the number of bytes still available}
  3546. MediumSequentialFeedBytesLeft := (MediumBlockPoolSize - MediumBlockPoolHeaderSize) - AFirstBlockSize;
  3547. {Get the result}
  3548. Result := Pointer(PByte(LNewPool) + MediumBlockPoolSize - AFirstBlockSize);
  3549. LastSequentiallyFedMediumBlock := Result;
  3550. {Store the block header}
  3551. PNativeUInt(PByte(Result) - BlockHeaderSize)^ := AFirstBlockSize or IsMediumBlockFlag;
  3552. end
  3553. else
  3554. begin
  3555. {Out of memory}
  3556. MediumSequentialFeedBytesLeft := 0;
  3557. Result := nil;
  3558. end;
  3559. end;
  3560. {-----------------Large Block Management------------------}
  3561. {Locks the large blocks}
  3562. procedure LockLargeBlocks;
  3563. begin
  3564. {Lock the large blocks}
  3565. {$ifndef AssumeMultiThreaded}
  3566. if IsMultiThread then
  3567. {$endif}
  3568. begin
  3569. while LockCmpxchg(0, 1, @LargeBlocksLocked) <> 0 do
  3570. begin
  3571. {$ifdef NeverSleepOnThreadContention}
  3572. {$ifdef UseSwitchToThread}
  3573. SwitchToThread;
  3574. {$endif}
  3575. {$else}
  3576. Sleep(InitialSleepTime);
  3577. if LockCmpxchg(0, 1, @LargeBlocksLocked) = 0 then
  3578. Break;
  3579. Sleep(AdditionalSleepTime);
  3580. {$endif}
  3581. end;
  3582. end;
  3583. end;
  3584. {Allocates a Large block of at least ASize (actual size may be larger to
  3585. allow for alignment etc.). ASize must be the actual user requested size. This
  3586. procedure will pad it to the appropriate page boundary and also add the space
  3587. required by the header.}
  3588. function AllocateLargeBlock(ASize: NativeUInt): Pointer;
  3589. var
  3590. LLargeUsedBlockSize: NativeUInt;
  3591. LOldFirstLargeBlock: PLargeBlockHeader;
  3592. begin
  3593. {Pad the block size to include the header and granularity. We also add a
  3594. SizeOf(Pointer) overhead so a huge block size is a multiple of 16 bytes less
  3595. SizeOf(Pointer) (so we can use a single move function for reallocating all
  3596. block types)}
  3597. LLargeUsedBlockSize := (ASize + LargeBlockHeaderSize + LargeBlockGranularity - 1 + BlockHeaderSize)
  3598. and -LargeBlockGranularity;
  3599. {Get the Large block}
  3600. Result := VirtualAlloc(nil, LLargeUsedBlockSize, MEM_COMMIT or MEM_TOP_DOWN,
  3601. PAGE_READWRITE);
  3602. {Set the Large block fields}
  3603. if Result <> nil then
  3604. begin
  3605. {Set the large block size and flags}
  3606. PLargeBlockHeader(Result).UserAllocatedSize := ASize;
  3607. PLargeBlockHeader(Result).BlockSizeAndFlags := LLargeUsedBlockSize or IsLargeBlockFlag;
  3608. {Insert the large block into the linked list of large blocks}
  3609. LockLargeBlocks;
  3610. LOldFirstLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  3611. PLargeBlockHeader(Result).PreviousLargeBlockHeader := @LargeBlocksCircularList;
  3612. LargeBlocksCircularList.NextLargeBlockHeader := Result;
  3613. PLargeBlockHeader(Result).NextLargeBlockHeader := LOldFirstLargeBlock;
  3614. LOldFirstLargeBlock.PreviousLargeBlockHeader := Result;
  3615. LargeBlocksLocked := False;
  3616. {Add the size of the header}
  3617. Inc(PByte(Result), LargeBlockHeaderSize);
  3618. {$ifdef FullDebugMode}
  3619. {Since large blocks are never reused, the user area is not initialized to
  3620. the debug fill pattern, but the debug header and footer must be set.}
  3621. PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
  3622. PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
  3623. {$endif}
  3624. end;
  3625. end;
  3626. {Frees a large block, returning 0 on success, -1 otherwise}
  3627. function FreeLargeBlock(APointer: Pointer): Integer;
  3628. var
  3629. LPreviousLargeBlockHeader, LNextLargeBlockHeader: PLargeBlockHeader;
  3630. {$ifndef Linux}
  3631. LRemainingSize: NativeUInt;
  3632. LCurrentSegment: Pointer;
  3633. LMemInfo: TMemoryBasicInformation;
  3634. {$endif}
  3635. begin
  3636. {$ifdef ClearLargeBlocksBeforeReturningToOS}
  3637. FillChar(APointer^,
  3638. (PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags
  3639. and DropMediumAndLargeFlagsMask) - LargeBlockHeaderSize, 0);
  3640. {$endif}
  3641. {Point to the start of the large block}
  3642. APointer := Pointer(PByte(APointer) - LargeBlockHeaderSize);
  3643. {Get the previous and next large blocks}
  3644. LockLargeBlocks;
  3645. LPreviousLargeBlockHeader := PLargeBlockHeader(APointer).PreviousLargeBlockHeader;
  3646. LNextLargeBlockHeader := PLargeBlockHeader(APointer).NextLargeBlockHeader;
  3647. {$ifndef Linux}
  3648. {Is the large block segmented?}
  3649. if PLargeBlockHeader(APointer).BlockSizeAndFlags and LargeBlockIsSegmented = 0 then
  3650. begin
  3651. {$endif}
  3652. {Single segment large block: Try to free it}
  3653. if VirtualFree(APointer, 0, MEM_RELEASE) then
  3654. Result := 0
  3655. else
  3656. Result := -1;
  3657. {$ifndef Linux}
  3658. end
  3659. else
  3660. begin
  3661. {The large block is segmented - free all segments}
  3662. LCurrentSegment := APointer;
  3663. LRemainingSize := PLargeBlockHeader(APointer).BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  3664. Result := 0;
  3665. while True do
  3666. begin
  3667. {Get the size of the current segment}
  3668. VirtualQuery(LCurrentSegment, LMemInfo, SizeOf(LMemInfo));
  3669. {Free the segment}
  3670. if not VirtualFree(LCurrentSegment, 0, MEM_RELEASE) then
  3671. begin
  3672. Result := -1;
  3673. Break;
  3674. end;
  3675. {Done?}
  3676. if NativeUInt(LMemInfo.RegionSize) >= LRemainingSize then
  3677. Break;
  3678. {Decrement the remaining size}
  3679. Dec(LRemainingSize, NativeUInt(LMemInfo.RegionSize));
  3680. Inc(PByte(LCurrentSegment), NativeUInt(LMemInfo.RegionSize));
  3681. end;
  3682. end;
  3683. {$endif}
  3684. {Success?}
  3685. if Result = 0 then
  3686. begin
  3687. {Remove the large block from the linked list}
  3688. LNextLargeBlockHeader.PreviousLargeBlockHeader := LPreviousLargeBlockHeader;
  3689. LPreviousLargeBlockHeader.NextLargeBlockHeader := LNextLargeBlockHeader;
  3690. end;
  3691. {Unlock the large blocks}
  3692. LargeBlocksLocked := False;
  3693. end;
  3694. {$ifndef FullDebugMode}
  3695. {Reallocates a large block to at least the requested size. Returns the new
  3696. pointer, or nil on error}
  3697. function ReallocateLargeBlock(APointer: Pointer; ANewSize: NativeUInt): Pointer;
  3698. var
  3699. LOldAvailableSize, LBlockHeader, LOldUserSize, LMinimumUpsize,
  3700. LNewAllocSize: NativeUInt;
  3701. {$ifndef Linux}
  3702. LNewSegmentSize: NativeUInt;
  3703. LNextSegmentPointer: Pointer;
  3704. LMemInfo: TMemoryBasicInformation;
  3705. {$endif}
  3706. begin
  3707. {Get the block header}
  3708. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  3709. {Large block - size is (16 + 4) less than the allocated size}
  3710. LOldAvailableSize := (LBlockHeader and DropMediumAndLargeFlagsMask) - (LargeBlockHeaderSize + BlockHeaderSize);
  3711. {Is it an upsize or a downsize?}
  3712. if ANewSize > LOldAvailableSize then
  3713. begin
  3714. {This pointer is being reallocated to a larger block and therefore it is
  3715. logical to assume that it may be enlarged again. Since reallocations are
  3716. expensive, there is a minimum upsize percentage to avoid unnecessary
  3717. future move operations.}
  3718. {Add 25% for large block upsizes}
  3719. LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
  3720. if ANewSize < LMinimumUpsize then
  3721. LNewAllocSize := LMinimumUpsize
  3722. else
  3723. LNewAllocSize := ANewSize;
  3724. {$ifndef Linux}
  3725. {Can another large block segment be allocated directly after this segment,
  3726. thus negating the need to move the data?}
  3727. LNextSegmentPointer := Pointer(PByte(APointer) - LargeBlockHeaderSize + (LBlockHeader and DropMediumAndLargeFlagsMask));
  3728. VirtualQuery(LNextSegmentPointer, LMemInfo, SizeOf(LMemInfo));
  3729. if LMemInfo.State = MEM_FREE then
  3730. begin
  3731. {Round the region size to the previous 64K}
  3732. LMemInfo.RegionSize := LMemInfo.RegionSize and -LargeBlockGranularity;
  3733. {Enough space to grow in place?}
  3734. if NativeUInt(LMemInfo.RegionSize) > (ANewSize - LOldAvailableSize) then
  3735. begin
  3736. {There is enough space after the block to extend it - determine by how
  3737. much}
  3738. LNewSegmentSize := (LNewAllocSize - LOldAvailableSize + LargeBlockGranularity - 1) and -LargeBlockGranularity;
  3739. if LNewSegmentSize > LMemInfo.RegionSize then
  3740. LNewSegmentSize := LMemInfo.RegionSize;
  3741. {Attempy to reserve the address range (which will fail if another
  3742. thread has just reserved it) and commit it immediately afterwards.}
  3743. if (VirtualAlloc(LNextSegmentPointer, LNewSegmentSize, MEM_RESERVE, PAGE_READWRITE) <> nil)
  3744. and (VirtualAlloc(LNextSegmentPointer, LNewSegmentSize, MEM_COMMIT, PAGE_READWRITE) <> nil) then
  3745. begin
  3746. {Update the requested size}
  3747. PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  3748. PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags :=
  3749. (PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags + LNewSegmentSize)
  3750. or LargeBlockIsSegmented;
  3751. {Success}
  3752. Result := APointer;
  3753. Exit;
  3754. end;
  3755. end;
  3756. end;
  3757. {$endif}
  3758. {Could not resize in place: Allocate the new block}
  3759. Result := FastGetMem(LNewAllocSize);
  3760. if Result <> nil then
  3761. begin
  3762. {If it's a large block - store the actual user requested size (it may
  3763. not be if the block that is being reallocated from was previously
  3764. downsized)}
  3765. if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
  3766. PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  3767. {The user allocated size is stored for large blocks}
  3768. LOldUserSize := PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize;
  3769. {The number of bytes to move is the old user size.}
  3770. {$ifdef UseCustomVariableSizeMoveRoutines}
  3771. MoveX16LP(APointer^, Result^, LOldUserSize);
  3772. {$else}
  3773. System.Move(APointer^, Result^, LOldUserSize);
  3774. {$endif}
  3775. {Free the old block}
  3776. FastFreeMem(APointer);
  3777. end;
  3778. end
  3779. else
  3780. begin
  3781. {It's a downsize: do we need to reallocate? Only if the new size is less
  3782. than half the old size}
  3783. if ANewSize >= (LOldAvailableSize shr 1) then
  3784. begin
  3785. {No need to reallocate}
  3786. Result := APointer;
  3787. {Update the requested size}
  3788. PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  3789. end
  3790. else
  3791. begin
  3792. {The block is less than half the old size, and the current size is
  3793. greater than the minimum block size allowing a downsize: reallocate}
  3794. Result := FastGetMem(ANewSize);
  3795. if Result <> nil then
  3796. begin
  3797. {Still a large block? -> Set the user size}
  3798. if ANewSize > (MaximumMediumBlockSize - BlockHeaderSize) then
  3799. PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  3800. {Move the data across}
  3801. {$ifdef UseCustomVariableSizeMoveRoutines}
  3802. {$ifdef Align16Bytes}
  3803. MoveX16LP(APointer^, Result^, ANewSize);
  3804. {$else}
  3805. MoveX8LP(APointer^, Result^, ANewSize);
  3806. {$endif}
  3807. {$else}
  3808. System.Move(APointer^, Result^, ANewSize);
  3809. {$endif}
  3810. {Free the old block}
  3811. FastFreeMem(APointer);
  3812. end;
  3813. end;
  3814. end;
  3815. end;
  3816. {$endif}
  3817. {---------------------Replacement Memory Manager Interface---------------------}
  3818. {Replacement for SysGetMem}
  3819. function FastGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  3820. {$ifndef ASMVersion}
  3821. var
  3822. LMediumBlock{$ifndef FullDebugMode}, LNextFreeBlock, LSecondSplit{$endif}: PMediumFreeBlock;
  3823. LNextMediumBlockHeader: PNativeUInt;
  3824. LBlockSize, LAvailableBlockSize{$ifndef FullDebugMode}, LSecondSplitSize{$endif},
  3825. LSequentialFeedFreeSize: NativeUInt;
  3826. LPSmallBlockType: PSmallBlockType;
  3827. LPSmallBlockPool, LPNewFirstPool: PSmallBlockPoolHeader;
  3828. LNewFirstFreeBlock: Pointer;
  3829. LPMediumBin: PMediumFreeBlock;
  3830. LBinNumber, {$ifndef FullDebugMode}LBinGroupsMasked, {$endif}LBinGroupMasked,
  3831. LBinGroupNumber: Cardinal;
  3832. begin
  3833. {Is it a small block? -> Take the header size into account when
  3834. determining the required block size}
  3835. if NativeUInt(ASize) <= (MaximumSmallBlockSize - BlockHeaderSize) then
  3836. begin
  3837. {-------------------------Allocate a small block---------------------------}
  3838. {Get the block type from the size}
  3839. LPSmallBlockType := PSmallBlockType(AllocSize2SmallBlockTypeIndX4[
  3840. (NativeUInt(ASize) + (BlockHeaderSize - 1)) div SmallBlockGranularity]
  3841. * (SizeOf(TSmallBlockType) div 4)
  3842. + UIntPtr(@SmallBlockTypes));
  3843. {Lock the block type}
  3844. {$ifndef AssumeMultiThreaded}
  3845. if IsMultiThread then
  3846. {$endif}
  3847. begin
  3848. while True do
  3849. begin
  3850. {Try to lock the small block type}
  3851. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  3852. Break;
  3853. {Try the next block type}
  3854. Inc(PByte(LPSmallBlockType), SizeOf(TSmallBlockType));
  3855. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  3856. Break;
  3857. {Try up to two sizes past the requested size}
  3858. Inc(PByte(LPSmallBlockType), SizeOf(TSmallBlockType));
  3859. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  3860. Break;
  3861. {All three sizes locked - given up and sleep}
  3862. Dec(PByte(LPSmallBlockType), 2 * SizeOf(TSmallBlockType));
  3863. {$ifdef NeverSleepOnThreadContention}
  3864. {$ifdef UseSwitchToThread}
  3865. SwitchToThread;
  3866. {$endif}
  3867. {$else}
  3868. {Both this block type and the next is in use: sleep}
  3869. Sleep(InitialSleepTime);
  3870. {Try the lock again}
  3871. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  3872. Break;
  3873. {Sleep longer}
  3874. Sleep(AdditionalSleepTime);
  3875. {$endif}
  3876. end;
  3877. end;
  3878. {Get the first pool with free blocks}
  3879. LPSmallBlockPool := LPSmallBlockType.NextPartiallyFreePool;
  3880. {Is the pool valid?}
  3881. if UIntPtr(LPSmallBlockPool) <> UIntPtr(LPSmallBlockType) then
  3882. begin
  3883. {Get the first free offset}
  3884. Result := LPSmallBlockPool.FirstFreeBlock;
  3885. {Get the new first free block}
  3886. LNewFirstFreeBlock := PPointer(PByte(Result) - BlockHeaderSize)^;
  3887. {$ifdef CheckHeapForCorruption}
  3888. {The block should be free}
  3889. if (NativeUInt(LNewFirstFreeBlock) and ExtractSmallFlagsMask) <> IsFreeBlockFlag then
  3890. {$ifdef BCB6OrDelphi7AndUp}
  3891. System.Error(reInvalidPtr);
  3892. {$else}
  3893. System.RunError(reInvalidPtr);
  3894. {$endif}
  3895. {$endif}
  3896. LNewFirstFreeBlock := Pointer(UIntPtr(LNewFirstFreeBlock) and DropSmallFlagsMask);
  3897. {Increment the number of used blocks}
  3898. Inc(LPSmallBlockPool.BlocksInUse);
  3899. {Set the new first free block}
  3900. LPSmallBlockPool.FirstFreeBlock := LNewFirstFreeBlock;
  3901. {Is the pool now full?}
  3902. if LNewFirstFreeBlock = nil then
  3903. begin
  3904. {Pool is full - remove it from the partially free list}
  3905. LPNewFirstPool := LPSmallBlockPool.NextPartiallyFreePool;
  3906. LPSmallBlockType.NextPartiallyFreePool := LPNewFirstPool;
  3907. LPNewFirstPool.PreviousPartiallyFreePool := PSmallBlockPoolHeader(LPSmallBlockType);
  3908. end;
  3909. end
  3910. else
  3911. begin
  3912. {Try to feed a small block sequentially}
  3913. Result := LPSmallBlockType.NextSequentialFeedBlockAddress;
  3914. {Can another block fit?}
  3915. if UIntPtr(Result) <= UIntPtr(LPSmallBlockType.MaxSequentialFeedBlockAddress) then
  3916. begin
  3917. {Get the sequential feed block pool}
  3918. LPSmallBlockPool := LPSmallBlockType.CurrentSequentialFeedPool;
  3919. {Increment the number of used blocks in the sequential feed pool}
  3920. Inc(LPSmallBlockPool.BlocksInUse);
  3921. {Store the next sequential feed block address}
  3922. LPSmallBlockType.NextSequentialFeedBlockAddress := Pointer(PByte(Result) + LPSmallBlockType.BlockSize);
  3923. end
  3924. else
  3925. begin
  3926. {Need to allocate a pool: Lock the medium blocks}
  3927. LockMediumBlocks;
  3928. {$ifndef FullDebugMode}
  3929. {Are there any available blocks of a suitable size?}
  3930. LBinGroupsMasked := MediumBlockBinGroupBitmap and ($ffffff00 or LPSmallBlockType.AllowedGroupsForBlockPoolBitmap);
  3931. if LBinGroupsMasked <> 0 then
  3932. begin
  3933. {Get the bin group with free blocks}
  3934. LBinGroupNumber := FindFirstSetBit(LBinGroupsMasked);
  3935. {Get the bin in the group with free blocks}
  3936. LBinNumber := FindFirstSetBit(MediumBlockBinBitmaps[LBinGroupNumber])
  3937. + LBinGroupNumber * 32;
  3938. LPMediumBin := @MediumBlockBins[LBinNumber];
  3939. {Get the first block in the bin}
  3940. LMediumBlock := LPMediumBin.NextFreeBlock;
  3941. {Remove the first block from the linked list (LIFO)}
  3942. LNextFreeBlock := LMediumBlock.NextFreeBlock;
  3943. LPMediumBin.NextFreeBlock := LNextFreeBlock;
  3944. LNextFreeBlock.PreviousFreeBlock := LPMediumBin;
  3945. {Is this bin now empty?}
  3946. if LNextFreeBlock = LPMediumBin then
  3947. begin
  3948. {Flag this bin as empty}
  3949. MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
  3950. and (not (1 shl (LBinNumber and 31)));
  3951. {Is the group now entirely empty?}
  3952. if MediumBlockBinBitmaps[LBinGroupNumber] = 0 then
  3953. begin
  3954. {Flag this group as empty}
  3955. MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
  3956. and (not (1 shl LBinGroupNumber));
  3957. end;
  3958. end;
  3959. {Get the size of the available medium block}
  3960. LBlockSize := PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  3961. {$ifdef CheckHeapForCorruption}
  3962. {Check that this block is actually free and the next and previous blocks
  3963. are both in use.}
  3964. if ((PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag))
  3965. or ((PNativeUInt(PByte(LMediumBlock) + (PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and IsFreeBlockFlag) <> 0)
  3966. then
  3967. begin
  3968. {$ifdef BCB6OrDelphi7AndUp}
  3969. System.Error(reInvalidPtr);
  3970. {$else}
  3971. System.RunError(reInvalidPtr);
  3972. {$endif}
  3973. end;
  3974. {$endif}
  3975. {Should the block be split?}
  3976. if LBlockSize >= MaximumSmallBlockPoolSize then
  3977. begin
  3978. {Get the size of the second split}
  3979. LSecondSplitSize := LBlockSize - LPSmallBlockType.OptimalBlockPoolSize;
  3980. {Adjust the block size}
  3981. LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
  3982. {Split the block in two}
  3983. LSecondSplit := PMediumFreeBlock(PByte(LMediumBlock) + LBlockSize);
  3984. PNativeUInt(PByte(LSecondSplit) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  3985. {Store the size of the second split as the second last dword/qword}
  3986. PNativeUInt(PByte(LSecondSplit) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
  3987. {Put the remainder in a bin (it will be big enough)}
  3988. InsertMediumBlockIntoBin(LSecondSplit, LSecondSplitSize);
  3989. end
  3990. else
  3991. begin
  3992. {Mark this block as used in the block following it}
  3993. LNextMediumBlockHeader := PNativeUInt(PByte(LMediumBlock) + LBlockSize - BlockHeaderSize);
  3994. LNextMediumBlockHeader^ := LNextMediumBlockHeader^ and (not PreviousMediumBlockIsFreeFlag);
  3995. end;
  3996. end
  3997. else
  3998. begin
  3999. {$endif}
  4000. {Check the sequential feed medium block pool for space}
  4001. LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
  4002. if LSequentialFeedFreeSize >= LPSmallBlockType.MinimumBlockPoolSize then
  4003. begin
  4004. {Enough sequential feed space: Will the remainder be usable?}
  4005. if LSequentialFeedFreeSize >= (LPSmallBlockType.OptimalBlockPoolSize + MinimumMediumBlockSize) then
  4006. begin
  4007. LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
  4008. end
  4009. else
  4010. LBlockSize := LSequentialFeedFreeSize;
  4011. {Get the block}
  4012. LMediumBlock := Pointer(PByte(LastSequentiallyFedMediumBlock) - LBlockSize);
  4013. {Update the sequential feed parameters}
  4014. LastSequentiallyFedMediumBlock := LMediumBlock;
  4015. MediumSequentialFeedBytesLeft := LSequentialFeedFreeSize - LBlockSize;
  4016. end
  4017. else
  4018. begin
  4019. {Need to allocate a new sequential feed medium block pool: use the
  4020. optimal size for this small block pool}
  4021. LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
  4022. {Allocate the medium block pool}
  4023. LMediumBlock := AllocNewSequentialFeedMediumPool(LBlockSize);
  4024. if LMediumBlock = nil then
  4025. begin
  4026. {Out of memory}
  4027. {Unlock the medium blocks}
  4028. MediumBlocksLocked := False;
  4029. {Unlock the block type}
  4030. LPSmallBlockType.BlockTypeLocked := False;
  4031. {Failed}
  4032. Result := nil;
  4033. {done}
  4034. Exit;
  4035. end;
  4036. end;
  4037. {$ifndef FullDebugMode}
  4038. end;
  4039. {$endif}
  4040. {Mark this block as in use}
  4041. {Set the size and flags for this block}
  4042. PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag or IsSmallBlockPoolInUseFlag;
  4043. {Unlock medium blocks}
  4044. MediumBlocksLocked := False;
  4045. {Set up the block pool}
  4046. LPSmallBlockPool := PSmallBlockPoolHeader(LMediumBlock);
  4047. LPSmallBlockPool.BlockType := LPSmallBlockType;
  4048. LPSmallBlockPool.FirstFreeBlock := nil;
  4049. LPSmallBlockPool.BlocksInUse := 1;
  4050. {Set it up for sequential block serving}
  4051. LPSmallBlockType.CurrentSequentialFeedPool := LPSmallBlockPool;
  4052. Result := Pointer(PByte(LPSmallBlockPool) + SmallBlockPoolHeaderSize);
  4053. LPSmallBlockType.NextSequentialFeedBlockAddress := Pointer(PByte(Result) + LPSmallBlockType.BlockSize);
  4054. LPSmallBlockType.MaxSequentialFeedBlockAddress := Pointer(PByte(LPSmallBlockPool) + LBlockSize - LPSmallBlockType.BlockSize);
  4055. end;
  4056. {$ifdef FullDebugMode}
  4057. {Clear the user area of the block}
  4058. DebugFillMem(Pointer(PByte(Result) + (SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt)))^,
  4059. LPSmallBlockType.BlockSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
  4060. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  4061. {Block was fed sequentially - we need to set a valid debug header. Use
  4062. the block address.}
  4063. PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
  4064. PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
  4065. {$endif}
  4066. end;
  4067. {Unlock the block type}
  4068. LPSmallBlockType.BlockTypeLocked := False;
  4069. {Set the block header}
  4070. PNativeUInt(PByte(Result) - BlockHeaderSize)^ := UIntPtr(LPSmallBlockPool);
  4071. end
  4072. else
  4073. begin
  4074. {Medium block or Large block?}
  4075. if NativeUInt(ASize) <= (MaximumMediumBlockSize - BlockHeaderSize) then
  4076. begin
  4077. {------------------------Allocate a medium block--------------------------}
  4078. {Get the block size and bin number for this block size. Block sizes are
  4079. rounded up to the next bin size.}
  4080. LBlockSize := ((NativeUInt(ASize) + (MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset))
  4081. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  4082. {Get the bin number}
  4083. LBinNumber := (LBlockSize - MinimumMediumBlockSize) div MediumBlockGranularity;
  4084. {Lock the medium blocks}
  4085. LockMediumBlocks;
  4086. {Calculate the bin group}
  4087. LBinGroupNumber := LBinNumber div 32;
  4088. {Is there a suitable block inside this group?}
  4089. LBinGroupMasked := MediumBlockBinBitmaps[LBinGroupNumber] and -(1 shl (LBinNumber and 31));
  4090. if LBinGroupMasked <> 0 then
  4091. begin
  4092. {Get the actual bin number}
  4093. LBinNumber := FindFirstSetBit(LBinGroupMasked) + LBinGroupNumber * 32;
  4094. end
  4095. else
  4096. begin
  4097. {$ifndef FullDebugMode}
  4098. {Try all groups greater than this group}
  4099. LBinGroupsMasked := MediumBlockBinGroupBitmap and -(2 shl LBinGroupNumber);
  4100. if LBinGroupsMasked <> 0 then
  4101. begin
  4102. {There is a suitable group with space: get the bin number}
  4103. LBinGroupNumber := FindFirstSetBit(LBinGroupsMasked);
  4104. {Get the bin in the group with free blocks}
  4105. LBinNumber := FindFirstSetBit(MediumBlockBinBitmaps[LBinGroupNumber])
  4106. + LBinGroupNumber * 32;
  4107. end
  4108. else
  4109. begin
  4110. {$endif}
  4111. {There are no bins with a suitable block: Sequentially feed the required block}
  4112. LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
  4113. if LSequentialFeedFreeSize >= LBlockSize then
  4114. begin
  4115. {$ifdef FullDebugMode}
  4116. {In full debug mode a medium block must have enough bytes to fit
  4117. all the debug info, so we must make sure there are no tiny medium
  4118. blocks at the start of the pool.}
  4119. if LSequentialFeedFreeSize - LBlockSize < (FullDebugBlockOverhead + BlockHeaderSize) then
  4120. LBlockSize := LSequentialFeedFreeSize;
  4121. {$endif}
  4122. {Block can be fed sequentially}
  4123. Result := Pointer(PByte(LastSequentiallyFedMediumBlock) - LBlockSize);
  4124. {Store the last sequentially fed block}
  4125. LastSequentiallyFedMediumBlock := Result;
  4126. {Store the remaining bytes}
  4127. MediumSequentialFeedBytesLeft := LSequentialFeedFreeSize - LBlockSize;
  4128. {Set the flags for the block}
  4129. PNativeUInt(PByte(Result) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag;
  4130. end
  4131. else
  4132. begin
  4133. {Need to allocate a new sequential feed block}
  4134. Result := AllocNewSequentialFeedMediumPool(LBlockSize);
  4135. end;
  4136. {$ifdef FullDebugMode}
  4137. {Block was fed sequentially - we need to set a valid debug header}
  4138. if Result <> nil then
  4139. begin
  4140. PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
  4141. PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
  4142. {Clear the user area of the block}
  4143. DebugFillMem(Pointer(PByte(Result) + SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt))^,
  4144. LBlockSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
  4145. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  4146. end;
  4147. {$endif}
  4148. {Done}
  4149. MediumBlocksLocked := False;
  4150. Exit;
  4151. {$ifndef FullDebugMode}
  4152. end;
  4153. {$endif}
  4154. end;
  4155. {If we get here we have a valid LBinGroupNumber and LBinNumber:
  4156. Use the first block in the bin, splitting it if necessary}
  4157. {Get a pointer to the bin}
  4158. LPMediumBin := @MediumBlockBins[LBinNumber];
  4159. {Get the result}
  4160. Result := LPMediumBin.NextFreeBlock;
  4161. {$ifdef CheckHeapForCorruption}
  4162. {Check that this block is actually free and the next and previous blocks
  4163. are both in use (except in full debug mode).}
  4164. if ((PNativeUInt(PByte(Result) - BlockHeaderSize)^ and {$ifndef FullDebugMode}ExtractMediumAndLargeFlagsMask{$else}(IsMediumBlockFlag or IsFreeBlockFlag){$endif}) <> (IsFreeBlockFlag or IsMediumBlockFlag))
  4165. {$ifndef FullDebugMode}
  4166. or ((PNativeUInt(PByte(Result) + (PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and (ExtractMediumAndLargeFlagsMask - IsSmallBlockPoolInUseFlag)) <> (IsMediumBlockFlag or PreviousMediumBlockIsFreeFlag))
  4167. {$endif}
  4168. then
  4169. begin
  4170. {$ifdef BCB6OrDelphi7AndUp}
  4171. System.Error(reInvalidPtr);
  4172. {$else}
  4173. System.RunError(reInvalidPtr);
  4174. {$endif}
  4175. end;
  4176. {$endif}
  4177. {Remove the block from the bin containing it}
  4178. RemoveMediumFreeBlock(Result);
  4179. {Get the block size}
  4180. LAvailableBlockSize := PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  4181. {$ifndef FullDebugMode}
  4182. {Is it an exact fit or not?}
  4183. LSecondSplitSize := LAvailableBlockSize - LBlockSize;
  4184. if LSecondSplitSize <> 0 then
  4185. begin
  4186. {Split the block in two}
  4187. LSecondSplit := PMediumFreeBlock(PByte(Result) + LBlockSize);
  4188. {Set the size of the second split}
  4189. PNativeUInt(PByte(LSecondSplit) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  4190. {Store the size of the second split}
  4191. PNativeUInt(PByte(LSecondSplit) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
  4192. {Put the remainder in a bin if it is big enough}
  4193. if LSecondSplitSize >= MinimumMediumBlockSize then
  4194. InsertMediumBlockIntoBin(LSecondSplit, LSecondSplitSize);
  4195. end
  4196. else
  4197. begin
  4198. {$else}
  4199. {In full debug mode blocks are never split or coalesced}
  4200. LBlockSize := LAvailableBlockSize;
  4201. {$endif}
  4202. {Mark this block as used in the block following it}
  4203. LNextMediumBlockHeader := Pointer(PByte(Result) + LBlockSize - BlockHeaderSize);
  4204. {$ifndef FullDebugMode}
  4205. {$ifdef CheckHeapForCorruption}
  4206. {The next block must be in use}
  4207. if (LNextMediumBlockHeader^ and (ExtractMediumAndLargeFlagsMask - IsSmallBlockPoolInUseFlag)) <> (IsMediumBlockFlag or PreviousMediumBlockIsFreeFlag) then
  4208. {$ifdef BCB6OrDelphi7AndUp}
  4209. System.Error(reInvalidPtr);
  4210. {$else}
  4211. System.RunError(reInvalidPtr);
  4212. {$endif}
  4213. {$endif}
  4214. {$endif}
  4215. LNextMediumBlockHeader^ :=
  4216. LNextMediumBlockHeader^ and (not PreviousMediumBlockIsFreeFlag);
  4217. {$ifndef FullDebugMode}
  4218. end;
  4219. {Set the size and flags for this block}
  4220. PNativeUInt(PByte(Result) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag;
  4221. {$else}
  4222. {In full debug mode blocks are never split or coalesced}
  4223. Dec(PNativeUInt(PByte(Result) - BlockHeaderSize)^, IsFreeBlockFlag);
  4224. {$endif}
  4225. {Unlock the medium blocks}
  4226. MediumBlocksLocked := False;
  4227. end
  4228. else
  4229. begin
  4230. {Allocate a Large block}
  4231. if ASize > 0 then
  4232. Result := AllocateLargeBlock(ASize)
  4233. else
  4234. Result := nil;
  4235. end;
  4236. end;
  4237. end;
  4238. {$else}
  4239. {$ifdef 32Bit}
  4240. asm
  4241. {On entry:
  4242. eax = ASize}
  4243. {Since most allocations are for small blocks, determine the small block type
  4244. index so long}
  4245. lea edx, [eax + BlockHeaderSize - 1]
  4246. {$ifdef Align16Bytes}
  4247. shr edx, 4
  4248. {$else}
  4249. shr edx, 3
  4250. {$endif}
  4251. {Is it a small block?}
  4252. cmp eax, (MaximumSmallBlockSize - BlockHeaderSize)
  4253. {Save ebx}
  4254. push ebx
  4255. {Get the IsMultiThread variable so long}
  4256. {$ifndef AssumeMultiThreaded}
  4257. mov cl, IsMultiThread
  4258. {$endif}
  4259. {Is it a small block?}
  4260. ja @NotASmallBlock
  4261. {Do we need to lock the block type?}
  4262. {$ifndef AssumeMultiThreaded}
  4263. test cl, cl
  4264. {$endif}
  4265. {Get the small block type in ebx}
  4266. movzx eax, byte ptr [AllocSize2SmallBlockTypeIndX4 + edx]
  4267. lea ebx, [SmallBlockTypes + eax * 8]
  4268. {Do we need to lock the block type?}
  4269. {$ifndef AssumeMultiThreaded}
  4270. jnz @LockBlockTypeLoop
  4271. {$else}
  4272. jmp @LockBlockTypeLoop
  4273. {Align branch target}
  4274. nop
  4275. nop
  4276. {$endif}
  4277. @GotLockOnSmallBlockType:
  4278. {Find the next free block: Get the first pool with free blocks in edx}
  4279. mov edx, TSmallBlockType[ebx].NextPartiallyFreePool
  4280. {Get the first free block (or the next sequential feed address if edx = ebx)}
  4281. mov eax, TSmallBlockPoolHeader[edx].FirstFreeBlock
  4282. {Get the drop flags mask in ecx so long}
  4283. mov ecx, DropSmallFlagsMask
  4284. {Is there a pool with free blocks?}
  4285. cmp edx, ebx
  4286. je @TrySmallSequentialFeed
  4287. {Increment the number of used blocks}
  4288. add TSmallBlockPoolHeader[edx].BlocksInUse, 1
  4289. {Get the new first free block}
  4290. and ecx, [eax - 4]
  4291. {Set the new first free block}
  4292. mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
  4293. {Set the block header}
  4294. mov [eax - 4], edx
  4295. {Is the chunk now full?}
  4296. jz @RemoveSmallPool
  4297. {Unlock the block type}
  4298. mov TSmallBlockType[ebx].BlockTypeLocked, False
  4299. {Restore ebx}
  4300. pop ebx
  4301. {All done}
  4302. ret
  4303. {Align branch target}
  4304. {$ifndef AssumeMultiThreaded}
  4305. nop
  4306. nop
  4307. {$endif}
  4308. nop
  4309. @TrySmallSequentialFeed:
  4310. {Try to feed a small block sequentially: Get the sequential feed block pool}
  4311. mov edx, TSmallBlockType[ebx].CurrentSequentialFeedPool
  4312. {Get the next sequential feed address so long}
  4313. movzx ecx, TSmallBlockType[ebx].BlockSize
  4314. add ecx, eax
  4315. {Can another block fit?}
  4316. cmp eax, TSmallBlockType[ebx].MaxSequentialFeedBlockAddress
  4317. ja @AllocateSmallBlockPool
  4318. {Increment the number of used blocks in the sequential feed pool}
  4319. add TSmallBlockPoolHeader[edx].BlocksInUse, 1
  4320. {Store the next sequential feed block address}
  4321. mov TSmallBlockType[ebx].NextSequentialFeedBlockAddress, ecx
  4322. {Unlock the block type}
  4323. mov TSmallBlockType[ebx].BlockTypeLocked, False
  4324. {Set the block header}
  4325. mov [eax - 4], edx
  4326. {Restore ebx}
  4327. pop ebx
  4328. {All done}
  4329. ret
  4330. {Align branch target}
  4331. nop
  4332. nop
  4333. nop
  4334. @RemoveSmallPool:
  4335. {Pool is full - remove it from the partially free list}
  4336. mov ecx, TSmallBlockPoolHeader[edx].NextPartiallyFreePool
  4337. mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, ebx
  4338. mov TSmallBlockType[ebx].NextPartiallyFreePool, ecx
  4339. {Unlock the block type}
  4340. mov TSmallBlockType[ebx].BlockTypeLocked, False
  4341. {Restore ebx}
  4342. pop ebx
  4343. {All done}
  4344. ret
  4345. {Align branch target}
  4346. nop
  4347. nop
  4348. @LockBlockTypeLoop:
  4349. mov eax, $100
  4350. {Attempt to grab the block type}
  4351. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  4352. je @GotLockOnSmallBlockType
  4353. {Try the next size}
  4354. add ebx, Type(TSmallBlockType)
  4355. mov eax, $100
  4356. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  4357. je @GotLockOnSmallBlockType
  4358. {Try the next size (up to two sizes larger)}
  4359. add ebx, Type(TSmallBlockType)
  4360. mov eax, $100
  4361. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  4362. je @GotLockOnSmallBlockType
  4363. {Block type and two sizes larger are all locked - give up and sleep}
  4364. sub ebx, 2 * Type(TSmallBlockType)
  4365. {$ifdef NeverSleepOnThreadContention}
  4366. {Pause instruction (improves performance on P4)}
  4367. rep nop
  4368. {$ifdef UseSwitchToThread}
  4369. call SwitchToThread
  4370. {$endif}
  4371. {Try again}
  4372. jmp @LockBlockTypeLoop
  4373. {Align branch target}
  4374. nop
  4375. {$ifndef UseSwitchToThread}
  4376. nop
  4377. {$endif}
  4378. {$else}
  4379. {Couldn't grab the block type - sleep and try again}
  4380. push InitialSleepTime
  4381. call Sleep
  4382. {Try again}
  4383. mov eax, $100
  4384. {Attempt to grab the block type}
  4385. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  4386. je @GotLockOnSmallBlockType
  4387. {Couldn't grab the block type - sleep and try again}
  4388. push AdditionalSleepTime
  4389. call Sleep
  4390. {Try again}
  4391. jmp @LockBlockTypeLoop
  4392. {Align branch target}
  4393. nop
  4394. nop
  4395. nop
  4396. {$endif}
  4397. @AllocateSmallBlockPool:
  4398. {save additional registers}
  4399. push esi
  4400. push edi
  4401. {Do we need to lock the medium blocks?}
  4402. {$ifndef AssumeMultiThreaded}
  4403. cmp IsMultiThread, False
  4404. je @MediumBlocksLockedForPool
  4405. {$endif}
  4406. call LockMediumBlocks
  4407. @MediumBlocksLockedForPool:
  4408. {Are there any available blocks of a suitable size?}
  4409. movsx esi, TSmallBlockType[ebx].AllowedGroupsForBlockPoolBitmap
  4410. and esi, MediumBlockBinGroupBitmap
  4411. jz @NoSuitableMediumBlocks
  4412. {Get the bin group number with free blocks in eax}
  4413. bsf eax, esi
  4414. {Get the bin number in ecx}
  4415. lea esi, [eax * 8]
  4416. mov ecx, dword ptr [MediumBlockBinBitmaps + eax * 4]
  4417. bsf ecx, ecx
  4418. lea ecx, [ecx + esi * 4]
  4419. {Get a pointer to the bin in edi}
  4420. lea edi, [MediumBlockBins + ecx * 8]
  4421. {Get the free block in esi}
  4422. mov esi, TMediumFreeBlock[edi].NextFreeBlock
  4423. {Remove the first block from the linked list (LIFO)}
  4424. mov edx, TMediumFreeBlock[esi].NextFreeBlock
  4425. mov TMediumFreeBlock[edi].NextFreeBlock, edx
  4426. mov TMediumFreeBlock[edx].PreviousFreeBlock, edi
  4427. {Is this bin now empty?}
  4428. cmp edi, edx
  4429. jne @MediumBinNotEmpty
  4430. {eax = bin group number, ecx = bin number, edi = @bin, esi = free block, ebx = block type}
  4431. {Flag this bin as empty}
  4432. mov edx, -2
  4433. rol edx, cl
  4434. and dword ptr [MediumBlockBinBitmaps + eax * 4], edx
  4435. jnz @MediumBinNotEmpty
  4436. {Flag the group as empty}
  4437. btr MediumBlockBinGroupBitmap, eax
  4438. @MediumBinNotEmpty:
  4439. {esi = free block, ebx = block type}
  4440. {Get the size of the available medium block in edi}
  4441. mov edi, DropMediumAndLargeFlagsMask
  4442. and edi, [esi - 4]
  4443. cmp edi, MaximumSmallBlockPoolSize
  4444. jb @UseWholeBlock
  4445. {Split the block: get the size of the second part, new block size is the
  4446. optimal size}
  4447. mov edx, edi
  4448. movzx edi, TSmallBlockType[ebx].OptimalBlockPoolSize
  4449. sub edx, edi
  4450. {Split the block in two}
  4451. lea eax, [esi + edi]
  4452. lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  4453. mov [eax - 4], ecx
  4454. {Store the size of the second split as the second last dword}
  4455. mov [eax + edx - 8], edx
  4456. {Put the remainder in a bin (it will be big enough)}
  4457. call InsertMediumBlockIntoBin
  4458. jmp @GotMediumBlock
  4459. {Align branch target}
  4460. {$ifdef AssumeMultiThreaded}
  4461. nop
  4462. {$endif}
  4463. @NoSuitableMediumBlocks:
  4464. {Check the sequential feed medium block pool for space}
  4465. movzx ecx, TSmallBlockType[ebx].MinimumBlockPoolSize
  4466. mov edi, MediumSequentialFeedBytesLeft
  4467. cmp edi, ecx
  4468. jb @AllocateNewSequentialFeed
  4469. {Get the address of the last block that was fed}
  4470. mov esi, LastSequentiallyFedMediumBlock
  4471. {Enough sequential feed space: Will the remainder be usable?}
  4472. movzx ecx, TSmallBlockType[ebx].OptimalBlockPoolSize
  4473. lea edx, [ecx + MinimumMediumBlockSize]
  4474. cmp edi, edx
  4475. jb @NotMuchSpace
  4476. mov edi, ecx
  4477. @NotMuchSpace:
  4478. sub esi, edi
  4479. {Update the sequential feed parameters}
  4480. sub MediumSequentialFeedBytesLeft, edi
  4481. mov LastSequentiallyFedMediumBlock, esi
  4482. {Get the block pointer}
  4483. jmp @GotMediumBlock
  4484. {Align branch target}
  4485. @AllocateNewSequentialFeed:
  4486. {Need to allocate a new sequential feed medium block pool: use the
  4487. optimal size for this small block pool}
  4488. movzx eax, TSmallBlockType[ebx].OptimalBlockPoolSize
  4489. mov edi, eax
  4490. {Allocate the medium block pool}
  4491. call AllocNewSequentialFeedMediumPool
  4492. mov esi, eax
  4493. test eax, eax
  4494. jnz @GotMediumBlock
  4495. mov MediumBlocksLocked, al
  4496. mov TSmallBlockType[ebx].BlockTypeLocked, al
  4497. pop edi
  4498. pop esi
  4499. pop ebx
  4500. ret
  4501. {Align branch target}
  4502. @UseWholeBlock:
  4503. {esi = free block, ebx = block type, edi = block size}
  4504. {Mark this block as used in the block following it}
  4505. and byte ptr [esi + edi - 4], not PreviousMediumBlockIsFreeFlag
  4506. @GotMediumBlock:
  4507. {esi = free block, ebx = block type, edi = block size}
  4508. {Set the size and flags for this block}
  4509. lea ecx, [edi + IsMediumBlockFlag + IsSmallBlockPoolInUseFlag]
  4510. mov [esi - 4], ecx
  4511. {Unlock medium blocks}
  4512. xor eax, eax
  4513. mov MediumBlocksLocked, al
  4514. {Set up the block pool}
  4515. mov TSmallBlockPoolHeader[esi].BlockType, ebx
  4516. mov TSmallBlockPoolHeader[esi].FirstFreeBlock, eax
  4517. mov TSmallBlockPoolHeader[esi].BlocksInUse, 1
  4518. {Set it up for sequential block serving}
  4519. mov TSmallBlockType[ebx].CurrentSequentialFeedPool, esi
  4520. {Return the pointer to the first block}
  4521. lea eax, [esi + SmallBlockPoolHeaderSize]
  4522. movzx ecx, TSmallBlockType[ebx].BlockSize
  4523. lea edx, [eax + ecx]
  4524. mov TSmallBlockType[ebx].NextSequentialFeedBlockAddress, edx
  4525. add edi, esi
  4526. sub edi, ecx
  4527. mov TSmallBlockType[ebx].MaxSequentialFeedBlockAddress, edi
  4528. {Unlock the small block type}
  4529. mov TSmallBlockType[ebx].BlockTypeLocked, False
  4530. {Set the small block header}
  4531. mov [eax - 4], esi
  4532. {Restore registers}
  4533. pop edi
  4534. pop esi
  4535. pop ebx
  4536. {Done}
  4537. ret
  4538. {-------------------Medium block allocation-------------------}
  4539. {Align branch target}
  4540. nop
  4541. @NotASmallBlock:
  4542. cmp eax, (MaximumMediumBlockSize - BlockHeaderSize)
  4543. ja @IsALargeBlockRequest
  4544. {Get the bin size for this block size. Block sizes are
  4545. rounded up to the next bin size.}
  4546. lea ebx, [eax + MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset]
  4547. and ebx, -MediumBlockGranularity
  4548. add ebx, MediumBlockSizeOffset
  4549. {Do we need to lock the medium blocks?}
  4550. {$ifndef AssumeMultiThreaded}
  4551. test cl, cl
  4552. jz @MediumBlocksLocked
  4553. {$endif}
  4554. call LockMediumBlocks
  4555. @MediumBlocksLocked:
  4556. {Get the bin number in ecx and the group number in edx}
  4557. lea edx, [ebx - MinimumMediumBlockSize]
  4558. mov ecx, edx
  4559. shr edx, 8 + 5
  4560. shr ecx, 8
  4561. {Is there a suitable block inside this group?}
  4562. mov eax, -1
  4563. shl eax, cl
  4564. and eax, dword ptr [MediumBlockBinBitmaps + edx * 4]
  4565. jz @GroupIsEmpty
  4566. {Get the actual bin number}
  4567. and ecx, -32
  4568. bsf eax, eax
  4569. or ecx, eax
  4570. jmp @GotBinAndGroup
  4571. {Align branch target}
  4572. nop
  4573. @GroupIsEmpty:
  4574. {Try all groups greater than this group}
  4575. mov eax, -2
  4576. mov ecx, edx
  4577. shl eax, cl
  4578. and eax, MediumBlockBinGroupBitmap
  4579. jz @TrySequentialFeedMedium
  4580. {There is a suitable group with space: get the bin number}
  4581. bsf edx, eax
  4582. {Get the bin in the group with free blocks}
  4583. mov eax, dword ptr [MediumBlockBinBitmaps + edx * 4]
  4584. bsf ecx, eax
  4585. mov eax, edx
  4586. shl eax, 5
  4587. or ecx, eax
  4588. jmp @GotBinAndGroup
  4589. {Align branch target}
  4590. nop
  4591. @TrySequentialFeedMedium:
  4592. mov ecx, MediumSequentialFeedBytesLeft
  4593. {Block can be fed sequentially?}
  4594. sub ecx, ebx
  4595. jc @AllocateNewSequentialFeedForMedium
  4596. {Get the block address}
  4597. mov eax, LastSequentiallyFedMediumBlock
  4598. sub eax, ebx
  4599. mov LastSequentiallyFedMediumBlock, eax
  4600. {Store the remaining bytes}
  4601. mov MediumSequentialFeedBytesLeft, ecx
  4602. {Set the flags for the block}
  4603. or ebx, IsMediumBlockFlag
  4604. mov [eax - 4], ebx
  4605. jmp @MediumBlockGetDone
  4606. {Align branch target}
  4607. @AllocateNewSequentialFeedForMedium:
  4608. mov eax, ebx
  4609. call AllocNewSequentialFeedMediumPool
  4610. @MediumBlockGetDone:
  4611. mov MediumBlocksLocked, False
  4612. pop ebx
  4613. ret
  4614. {Align branch target}
  4615. @GotBinAndGroup:
  4616. {ebx = block size, ecx = bin number, edx = group number}
  4617. push esi
  4618. push edi
  4619. {Get a pointer to the bin in edi}
  4620. lea edi, [MediumBlockBins + ecx * 8]
  4621. {Get the free block in esi}
  4622. mov esi, TMediumFreeBlock[edi].NextFreeBlock
  4623. {Remove the first block from the linked list (LIFO)}
  4624. mov eax, TMediumFreeBlock[esi].NextFreeBlock
  4625. mov TMediumFreeBlock[edi].NextFreeBlock, eax
  4626. mov TMediumFreeBlock[eax].PreviousFreeBlock, edi
  4627. {Is this bin now empty?}
  4628. cmp edi, eax
  4629. jne @MediumBinNotEmptyForMedium
  4630. {eax = bin group number, ecx = bin number, edi = @bin, esi = free block, ebx = block size}
  4631. {Flag this bin as empty}
  4632. mov eax, -2
  4633. rol eax, cl
  4634. and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
  4635. jnz @MediumBinNotEmptyForMedium
  4636. {Flag the group as empty}
  4637. btr MediumBlockBinGroupBitmap, edx
  4638. @MediumBinNotEmptyForMedium:
  4639. {esi = free block, ebx = block size}
  4640. {Get the size of the available medium block in edi}
  4641. mov edi, DropMediumAndLargeFlagsMask
  4642. and edi, [esi - 4]
  4643. {Get the size of the second split in edx}
  4644. mov edx, edi
  4645. sub edx, ebx
  4646. jz @UseWholeBlockForMedium
  4647. {Split the block in two}
  4648. lea eax, [esi + ebx]
  4649. lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  4650. mov [eax - 4], ecx
  4651. {Store the size of the second split as the second last dword}
  4652. mov [eax + edx - 8], edx
  4653. {Put the remainder in a bin}
  4654. cmp edx, MinimumMediumBlockSize
  4655. jb @GotMediumBlockForMedium
  4656. call InsertMediumBlockIntoBin
  4657. jmp @GotMediumBlockForMedium
  4658. {Align branch target}
  4659. nop
  4660. nop
  4661. nop
  4662. @UseWholeBlockForMedium:
  4663. {Mark this block as used in the block following it}
  4664. and byte ptr [esi + edi - 4], not PreviousMediumBlockIsFreeFlag
  4665. @GotMediumBlockForMedium:
  4666. {Set the size and flags for this block}
  4667. lea ecx, [ebx + IsMediumBlockFlag]
  4668. mov [esi - 4], ecx
  4669. {Unlock medium blocks}
  4670. mov MediumBlocksLocked, False
  4671. mov eax, esi
  4672. pop edi
  4673. pop esi
  4674. pop ebx
  4675. ret
  4676. {-------------------Large block allocation-------------------}
  4677. {Align branch target}
  4678. @IsALargeBlockRequest:
  4679. pop ebx
  4680. test eax, eax
  4681. jns AllocateLargeBlock
  4682. xor eax, eax
  4683. end;
  4684. {$else}
  4685. {64-bit BASM implementation}
  4686. asm
  4687. {On entry:
  4688. rcx = ASize}
  4689. .params 2
  4690. .pushnv rbx
  4691. .pushnv rsi
  4692. .pushnv rdi
  4693. {Since most allocations are for small blocks, determine the small block type
  4694. index so long}
  4695. lea edx, [ecx + BlockHeaderSize - 1]
  4696. {$ifdef Align16Bytes}
  4697. shr edx, 4
  4698. {$else}
  4699. shr edx, 3
  4700. {$endif}
  4701. {Preload the addresses of some small block structures}
  4702. lea r8, AllocSize2SmallBlockTypeIndX4
  4703. lea rbx, SmallBlockTypes
  4704. {$ifndef AssumeMultiThreaded}
  4705. {Get the IsMultiThread variable so long}
  4706. movzx esi, IsMultiThread
  4707. {$endif}
  4708. {Is it a small block?}
  4709. cmp rcx, (MaximumSmallBlockSize - BlockHeaderSize)
  4710. ja @NotASmallBlock
  4711. {Get the small block type pointer in rbx}
  4712. movzx ecx, byte ptr [r8 + rdx]
  4713. shl ecx, 4 //SizeOf(TSmallBlockType) = 64
  4714. add rbx, rcx
  4715. {Do we need to lock the block type?}
  4716. {$ifndef AssumeMultiThreaded}
  4717. test esi, esi
  4718. jnz @LockBlockTypeLoop
  4719. {$else}
  4720. jmp @LockBlockTypeLoop
  4721. {$endif}
  4722. @GotLockOnSmallBlockType:
  4723. {Find the next free block: Get the first pool with free blocks in rdx}
  4724. mov rdx, TSmallBlockType[rbx].NextPartiallyFreePool
  4725. {Get the first free block (or the next sequential feed address if rdx = rbx)}
  4726. mov rax, TSmallBlockPoolHeader[rdx].FirstFreeBlock
  4727. {Get the drop flags mask in rcx so long}
  4728. mov rcx, DropSmallFlagsMask
  4729. {Is there a pool with free blocks?}
  4730. cmp rdx, rbx
  4731. je @TrySmallSequentialFeed
  4732. {Increment the number of used blocks}
  4733. add TSmallBlockPoolHeader[rdx].BlocksInUse, 1
  4734. {Get the new first free block}
  4735. and rcx, [eax - BlockHeaderSize]
  4736. {Set the new first free block}
  4737. mov TSmallBlockPoolHeader[rdx].FirstFreeBlock, rcx
  4738. {Set the block header}
  4739. mov [rax - BlockHeaderSize], rdx
  4740. {Is the chunk now full?}
  4741. jz @RemoveSmallPool
  4742. {Unlock the block type}
  4743. mov TSmallBlockType[rbx].BlockTypeLocked, False
  4744. jmp @Done
  4745. @TrySmallSequentialFeed:
  4746. {Try to feed a small block sequentially: Get the sequential feed block pool}
  4747. mov rdx, TSmallBlockType[rbx].CurrentSequentialFeedPool
  4748. {Get the next sequential feed address so long}
  4749. movzx ecx, TSmallBlockType[rbx].BlockSize
  4750. add rcx, rax
  4751. {Can another block fit?}
  4752. cmp rax, TSmallBlockType[rbx].MaxSequentialFeedBlockAddress
  4753. ja @AllocateSmallBlockPool
  4754. {Increment the number of used blocks in the sequential feed pool}
  4755. add TSmallBlockPoolHeader[rdx].BlocksInUse, 1
  4756. {Store the next sequential feed block address}
  4757. mov TSmallBlockType[rbx].NextSequentialFeedBlockAddress, rcx
  4758. {Unlock the block type}
  4759. mov TSmallBlockType[rbx].BlockTypeLocked, False
  4760. {Set the block header}
  4761. mov [rax - BlockHeaderSize], rdx
  4762. jmp @Done
  4763. @RemoveSmallPool:
  4764. {Pool is full - remove it from the partially free list}
  4765. mov rcx, TSmallBlockPoolHeader[rdx].NextPartiallyFreePool
  4766. mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rbx
  4767. mov TSmallBlockType[rbx].NextPartiallyFreePool, rcx
  4768. {Unlock the block type}
  4769. mov TSmallBlockType[rbx].BlockTypeLocked, False
  4770. jmp @Done
  4771. @LockBlockTypeLoop:
  4772. mov eax, $100
  4773. {Attempt to grab the block type}
  4774. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  4775. je @GotLockOnSmallBlockType
  4776. {Try the next size}
  4777. add rbx, Type(TSmallBlockType)
  4778. mov eax, $100
  4779. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  4780. je @GotLockOnSmallBlockType
  4781. {Try the next size (up to two sizes larger)}
  4782. add rbx, Type(TSmallBlockType)
  4783. mov eax, $100
  4784. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  4785. je @GotLockOnSmallBlockType
  4786. {Block type and two sizes larger are all locked - give up and sleep}
  4787. sub rbx, 2 * Type(TSmallBlockType)
  4788. {$ifdef NeverSleepOnThreadContention}
  4789. {Pause instruction (improves performance on P4)}
  4790. pause
  4791. {$ifdef UseSwitchToThread}
  4792. call SwitchToThread
  4793. {$endif}
  4794. {Try again}
  4795. jmp @LockBlockTypeLoop
  4796. {$else}
  4797. {Couldn't grab the block type - sleep and try again}
  4798. mov ecx, InitialSleepTime
  4799. call Sleep
  4800. {Try again}
  4801. mov eax, $100
  4802. {Attempt to grab the block type}
  4803. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  4804. je @GotLockOnSmallBlockType
  4805. {Couldn't grab the block type - sleep and try again}
  4806. mov ecx, AdditionalSleepTime
  4807. call Sleep
  4808. {Try again}
  4809. jmp @LockBlockTypeLoop
  4810. {$endif}
  4811. @AllocateSmallBlockPool:
  4812. {Do we need to lock the medium blocks?}
  4813. {$ifndef AssumeMultiThreaded}
  4814. test esi, esi
  4815. jz @MediumBlocksLockedForPool
  4816. {$endif}
  4817. call LockMediumBlocks
  4818. @MediumBlocksLockedForPool:
  4819. {Are there any available blocks of a suitable size?}
  4820. movsx esi, TSmallBlockType[rbx].AllowedGroupsForBlockPoolBitmap
  4821. and esi, MediumBlockBinGroupBitmap
  4822. jz @NoSuitableMediumBlocks
  4823. {Get the bin group number with free blocks in eax}
  4824. bsf eax, esi
  4825. {Get the bin number in ecx}
  4826. lea r8, MediumBlockBinBitmaps
  4827. lea r9, [rax * 4]
  4828. mov ecx, [r8 + r9]
  4829. bsf ecx, ecx
  4830. lea ecx, [ecx + r9d * 8]
  4831. {Get a pointer to the bin in edi}
  4832. lea rdi, MediumBlockBins
  4833. lea esi, [ecx * 8]
  4834. lea rdi, [rdi + rsi * 2] //SizeOf(TMediumBlockBin) = 16
  4835. {Get the free block in rsi}
  4836. mov rsi, TMediumFreeBlock[rdi].NextFreeBlock
  4837. {Remove the first block from the linked list (LIFO)}
  4838. mov rdx, TMediumFreeBlock[rsi].NextFreeBlock
  4839. mov TMediumFreeBlock[rdi].NextFreeBlock, rdx
  4840. mov TMediumFreeBlock[rdx].PreviousFreeBlock, rdi
  4841. {Is this bin now empty?}
  4842. cmp rdi, rdx
  4843. jne @MediumBinNotEmpty
  4844. {r8 = @MediumBlockBinBitmaps, eax = bin group number,
  4845. r9 = bin group number * 4, ecx = bin number, edi = @bin, esi = free block,
  4846. ebx = block type}
  4847. {Flag this bin as empty}
  4848. mov edx, -2
  4849. rol edx, cl
  4850. and [r8 + r9], edx
  4851. jnz @MediumBinNotEmpty
  4852. {Flag the group as empty}
  4853. btr MediumBlockBinGroupBitmap, eax
  4854. @MediumBinNotEmpty:
  4855. {esi = free block, ebx = block type}
  4856. {Get the size of the available medium block in edi}
  4857. mov rdi, DropMediumAndLargeFlagsMask
  4858. and rdi, [rsi - BlockHeaderSize]
  4859. cmp edi, MaximumSmallBlockPoolSize
  4860. jb @UseWholeBlock
  4861. {Split the block: get the size of the second part, new block size is the
  4862. optimal size}
  4863. mov edx, edi
  4864. movzx edi, TSmallBlockType[rbx].OptimalBlockPoolSize
  4865. sub edx, edi
  4866. {Split the block in two}
  4867. lea rcx, [rsi + rdi]
  4868. lea rax, [rdx + IsMediumBlockFlag + IsFreeBlockFlag]
  4869. mov [rcx - BlockHeaderSize], rax
  4870. {Store the size of the second split as the second last qword}
  4871. mov [rcx + rdx - BlockHeaderSize * 2], rdx
  4872. {Put the remainder in a bin (it will be big enough)}
  4873. call InsertMediumBlockIntoBin
  4874. jmp @GotMediumBlock
  4875. @NoSuitableMediumBlocks:
  4876. {Check the sequential feed medium block pool for space}
  4877. movzx ecx, TSmallBlockType[rbx].MinimumBlockPoolSize
  4878. mov edi, MediumSequentialFeedBytesLeft
  4879. cmp edi, ecx
  4880. jb @AllocateNewSequentialFeed
  4881. {Get the address of the last block that was fed}
  4882. mov rsi, LastSequentiallyFedMediumBlock
  4883. {Enough sequential feed space: Will the remainder be usable?}
  4884. movzx ecx, TSmallBlockType[ebx].OptimalBlockPoolSize
  4885. lea edx, [ecx + MinimumMediumBlockSize]
  4886. cmp edi, edx
  4887. jb @NotMuchSpace
  4888. mov edi, ecx
  4889. @NotMuchSpace:
  4890. sub rsi, rdi
  4891. {Update the sequential feed parameters}
  4892. sub MediumSequentialFeedBytesLeft, edi
  4893. mov LastSequentiallyFedMediumBlock, rsi
  4894. {Get the block pointer}
  4895. jmp @GotMediumBlock
  4896. {Align branch target}
  4897. @AllocateNewSequentialFeed:
  4898. {Need to allocate a new sequential feed medium block pool: use the
  4899. optimal size for this small block pool}
  4900. movzx ecx, TSmallBlockType[ebx].OptimalBlockPoolSize
  4901. mov edi, ecx
  4902. {Allocate the medium block pool}
  4903. call AllocNewSequentialFeedMediumPool
  4904. mov rsi, rax
  4905. test rax, rax
  4906. jnz @GotMediumBlock
  4907. mov MediumBlocksLocked, al
  4908. mov TSmallBlockType[rbx].BlockTypeLocked, al
  4909. jmp @Done
  4910. @UseWholeBlock:
  4911. {rsi = free block, rbx = block type, edi = block size}
  4912. {Mark this block as used in the block following it}
  4913. and byte ptr [rsi + rdi - BlockHeaderSize], not PreviousMediumBlockIsFreeFlag
  4914. @GotMediumBlock:
  4915. {rsi = free block, rbx = block type, edi = block size}
  4916. {Set the size and flags for this block}
  4917. lea ecx, [edi + IsMediumBlockFlag + IsSmallBlockPoolInUseFlag]
  4918. mov [rsi - BlockHeaderSize], rcx
  4919. {Unlock medium blocks}
  4920. xor eax, eax
  4921. mov MediumBlocksLocked, al
  4922. {Set up the block pool}
  4923. mov TSmallBlockPoolHeader[rsi].BlockType, rbx
  4924. mov TSmallBlockPoolHeader[rsi].FirstFreeBlock, rax
  4925. mov TSmallBlockPoolHeader[rsi].BlocksInUse, 1
  4926. {Set it up for sequential block serving}
  4927. mov TSmallBlockType[rbx].CurrentSequentialFeedPool, rsi
  4928. {Return the pointer to the first block}
  4929. lea rax, [rsi + SmallBlockPoolHeaderSize]
  4930. movzx ecx, TSmallBlockType[rbx].BlockSize
  4931. lea rdx, [rax + rcx]
  4932. mov TSmallBlockType[rbx].NextSequentialFeedBlockAddress, rdx
  4933. add rdi, rsi
  4934. sub rdi, rcx
  4935. mov TSmallBlockType[rbx].MaxSequentialFeedBlockAddress, rdi
  4936. {Unlock the small block type}
  4937. mov TSmallBlockType[rbx].BlockTypeLocked, False
  4938. {Set the small block header}
  4939. mov [rax - BlockHeaderSize], rsi
  4940. jmp @Done
  4941. {-------------------Medium block allocation-------------------}
  4942. @NotASmallBlock:
  4943. cmp rcx, (MaximumMediumBlockSize - BlockHeaderSize)
  4944. ja @IsALargeBlockRequest
  4945. {Get the bin size for this block size. Block sizes are
  4946. rounded up to the next bin size.}
  4947. lea ebx, [ecx + MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset]
  4948. and ebx, -MediumBlockGranularity
  4949. add ebx, MediumBlockSizeOffset
  4950. {Do we need to lock the medium blocks?}
  4951. {$ifndef AssumeMultiThreaded}
  4952. test esi, esi
  4953. jz @MediumBlocksLocked
  4954. {$endif}
  4955. call LockMediumBlocks
  4956. @MediumBlocksLocked:
  4957. {Get the bin number in ecx and the group number in edx}
  4958. lea edx, [ebx - MinimumMediumBlockSize]
  4959. mov ecx, edx
  4960. shr edx, 8 + 5
  4961. shr ecx, 8
  4962. {Is there a suitable block inside this group?}
  4963. mov eax, -1
  4964. shl eax, cl
  4965. lea r8, MediumBlockBinBitmaps
  4966. and eax, [r8 + rdx * 4]
  4967. jz @GroupIsEmpty
  4968. {Get the actual bin number}
  4969. and ecx, -32
  4970. bsf eax, eax
  4971. or ecx, eax
  4972. jmp @GotBinAndGroup
  4973. @GroupIsEmpty:
  4974. {Try all groups greater than this group}
  4975. mov eax, -2
  4976. mov ecx, edx
  4977. shl eax, cl
  4978. and eax, MediumBlockBinGroupBitmap
  4979. jz @TrySequentialFeedMedium
  4980. {There is a suitable group with space: get the bin number}
  4981. bsf edx, eax
  4982. {Get the bin in the group with free blocks}
  4983. mov eax, [r8 + rdx * 4]
  4984. bsf ecx, eax
  4985. mov eax, edx
  4986. shl eax, 5
  4987. or ecx, eax
  4988. jmp @GotBinAndGroup
  4989. @TrySequentialFeedMedium:
  4990. mov ecx, MediumSequentialFeedBytesLeft
  4991. {Block can be fed sequentially?}
  4992. sub ecx, ebx
  4993. jc @AllocateNewSequentialFeedForMedium
  4994. {Get the block address}
  4995. mov rax, LastSequentiallyFedMediumBlock
  4996. sub rax, rbx
  4997. mov LastSequentiallyFedMediumBlock, rax
  4998. {Store the remaining bytes}
  4999. mov MediumSequentialFeedBytesLeft, ecx
  5000. {Set the flags for the block}
  5001. or rbx, IsMediumBlockFlag
  5002. mov [rax - BlockHeaderSize], rbx
  5003. jmp @MediumBlockGetDone
  5004. @AllocateNewSequentialFeedForMedium:
  5005. mov ecx, ebx
  5006. call AllocNewSequentialFeedMediumPool
  5007. @MediumBlockGetDone:
  5008. xor cl, cl
  5009. mov MediumBlocksLocked, cl //workaround for QC99023
  5010. jmp @Done
  5011. @GotBinAndGroup:
  5012. {ebx = block size, ecx = bin number, edx = group number}
  5013. {Get a pointer to the bin in edi}
  5014. lea rdi, MediumBlockBins
  5015. lea eax, [ecx + ecx]
  5016. lea rdi, [rdi + rax * 8]
  5017. {Get the free block in esi}
  5018. mov rsi, TMediumFreeBlock[rdi].NextFreeBlock
  5019. {Remove the first block from the linked list (LIFO)}
  5020. mov rax, TMediumFreeBlock[rsi].NextFreeBlock
  5021. mov TMediumFreeBlock[rdi].NextFreeBlock, rax
  5022. mov TMediumFreeBlock[rax].PreviousFreeBlock, rdi
  5023. {Is this bin now empty?}
  5024. cmp rdi, rax
  5025. jne @MediumBinNotEmptyForMedium
  5026. {edx = bin group number, ecx = bin number, rdi = @bin, rsi = free block, ebx = block size}
  5027. {Flag this bin as empty}
  5028. mov eax, -2
  5029. rol eax, cl
  5030. lea r8, MediumBlockBinBitmaps
  5031. and [r8 + rdx * 4], eax
  5032. jnz @MediumBinNotEmptyForMedium
  5033. {Flag the group as empty}
  5034. btr MediumBlockBinGroupBitmap, edx
  5035. @MediumBinNotEmptyForMedium:
  5036. {rsi = free block, ebx = block size}
  5037. {Get the size of the available medium block in edi}
  5038. mov rdi, DropMediumAndLargeFlagsMask
  5039. and rdi, [rsi - BlockHeaderSize]
  5040. {Get the size of the second split in edx}
  5041. mov edx, edi
  5042. sub edx, ebx
  5043. jz @UseWholeBlockForMedium
  5044. {Split the block in two}
  5045. lea rcx, [rsi + rbx]
  5046. lea rax, [rdx + IsMediumBlockFlag + IsFreeBlockFlag]
  5047. mov [rcx - BlockHeaderSize], rax
  5048. {Store the size of the second split as the second last dword}
  5049. mov [rcx + rdx - BlockHeaderSize * 2], rdx
  5050. {Put the remainder in a bin}
  5051. cmp edx, MinimumMediumBlockSize
  5052. jb @GotMediumBlockForMedium
  5053. call InsertMediumBlockIntoBin
  5054. jmp @GotMediumBlockForMedium
  5055. @UseWholeBlockForMedium:
  5056. {Mark this block as used in the block following it}
  5057. and byte ptr [rsi + rdi - BlockHeaderSize], not PreviousMediumBlockIsFreeFlag
  5058. @GotMediumBlockForMedium:
  5059. {Set the size and flags for this block}
  5060. lea rcx, [rbx + IsMediumBlockFlag]
  5061. mov [rsi - BlockHeaderSize], rcx
  5062. {Unlock medium blocks}
  5063. xor cl, cl
  5064. mov MediumBlocksLocked, cl //workaround for QC99023
  5065. mov rax, rsi
  5066. jmp @Done
  5067. {-------------------Large block allocation-------------------}
  5068. @IsALargeBlockRequest:
  5069. xor rax, rax
  5070. test rcx, rcx
  5071. js @Done
  5072. call AllocateLargeBlock
  5073. @Done:
  5074. end;
  5075. {$endif}
  5076. {$endif}
  5077. {$ifndef ASMVersion}
  5078. {Frees a medium block, returning 0 on success, -1 otherwise}
  5079. function FreeMediumBlock(APointer: Pointer): Integer;
  5080. var
  5081. LNextMediumBlock{$ifndef FullDebugMode}, LPreviousMediumBlock{$endif}: PMediumFreeBlock;
  5082. LNextMediumBlockSizeAndFlags: NativeUInt;
  5083. LBlockSize{$ifndef FullDebugMode}, LPreviousMediumBlockSize{$endif}: Cardinal;
  5084. {$ifndef FullDebugMode}
  5085. LPPreviousMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
  5086. {$endif}
  5087. LBlockHeader: NativeUInt;
  5088. begin
  5089. {Get the block header}
  5090. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  5091. {Get the medium block size}
  5092. LBlockSize := LBlockHeader and DropMediumAndLargeFlagsMask;
  5093. {Lock the medium blocks}
  5094. LockMediumBlocks;
  5095. {Can we combine this block with the next free block?}
  5096. LNextMediumBlock := PMediumFreeBlock(PByte(APointer) + LBlockSize);
  5097. LNextMediumBlockSizeAndFlags := PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^;
  5098. {$ifndef FullDebugMode}
  5099. {$ifdef CheckHeapForCorruption}
  5100. {Check that this block was flagged as in use in the next block}
  5101. if (LNextMediumBlockSizeAndFlags and PreviousMediumBlockIsFreeFlag) <> 0 then
  5102. {$ifdef BCB6OrDelphi7AndUp}
  5103. System.Error(reInvalidPtr);
  5104. {$else}
  5105. System.RunError(reInvalidPtr);
  5106. {$endif}
  5107. {$endif}
  5108. if (LNextMediumBlockSizeAndFlags and IsFreeBlockFlag) <> 0 then
  5109. begin
  5110. {Increase the size of this block}
  5111. Inc(LBlockSize, LNextMediumBlockSizeAndFlags and DropMediumAndLargeFlagsMask);
  5112. {Remove the next block as well}
  5113. if LNextMediumBlockSizeAndFlags >= MinimumMediumBlockSize then
  5114. RemoveMediumFreeBlock(LNextMediumBlock);
  5115. end
  5116. else
  5117. begin
  5118. {$endif}
  5119. {Reset the "previous in use" flag of the next block}
  5120. PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^ := LNextMediumBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
  5121. {$ifndef FullDebugMode}
  5122. end;
  5123. {Can we combine this block with the previous free block? We need to
  5124. re-read the flags since it could have changed before we could lock the
  5125. medium blocks.}
  5126. if (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and PreviousMediumBlockIsFreeFlag) <> 0 then
  5127. begin
  5128. {Get the size of the free block just before this one}
  5129. LPreviousMediumBlockSize := PNativeUInt(PByte(APointer) - 2 * BlockHeaderSize)^;
  5130. {Get the start of the previous block}
  5131. LPreviousMediumBlock := PMediumFreeBlock(PByte(APointer) - LPreviousMediumBlockSize);
  5132. {$ifdef CheckHeapForCorruption}
  5133. {Check that the previous block is actually free}
  5134. if (PNativeUInt(PByte(LPreviousMediumBlock) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag) then
  5135. {$ifdef BCB6OrDelphi7AndUp}
  5136. System.Error(reInvalidPtr);
  5137. {$else}
  5138. System.RunError(reInvalidPtr);
  5139. {$endif}
  5140. {$endif}
  5141. {Set the new block size}
  5142. Inc(LBlockSize, LPreviousMediumBlockSize);
  5143. {This is the new current block}
  5144. APointer := LPreviousMediumBlock;
  5145. {Remove the previous block from the linked list}
  5146. if LPreviousMediumBlockSize >= MinimumMediumBlockSize then
  5147. RemoveMediumFreeBlock(LPreviousMediumBlock);
  5148. end;
  5149. {$ifdef CheckHeapForCorruption}
  5150. {Check that the previous block is currently flagged as in use}
  5151. if (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and PreviousMediumBlockIsFreeFlag) <> 0 then
  5152. {$ifdef BCB6OrDelphi7AndUp}
  5153. System.Error(reInvalidPtr);
  5154. {$else}
  5155. System.RunError(reInvalidPtr);
  5156. {$endif}
  5157. {$endif}
  5158. {Is the entire medium block pool free, and there are other free blocks
  5159. that can fit the largest possible medium block? -> free it. (Except in
  5160. full debug mode where medium pools are never freed.)}
  5161. if (LBlockSize <> (MediumBlockPoolSize - MediumBlockPoolHeaderSize)) then
  5162. begin
  5163. {Store the size of the block as well as the flags}
  5164. PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := LBlockSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  5165. {$else}
  5166. {Mark the block as free}
  5167. Inc(PNativeUInt(PByte(APointer) - BlockHeaderSize)^, IsFreeBlockFlag);
  5168. {$endif}
  5169. {Store the trailing size marker}
  5170. PNativeUInt(PByte(APointer) + LBlockSize - 2 * BlockHeaderSize)^ := LBlockSize;
  5171. {Insert this block back into the bins: Size check not required here,
  5172. since medium blocks that are in use are not allowed to be
  5173. shrunk smaller than MinimumMediumBlockSize}
  5174. InsertMediumBlockIntoBin(APointer, LBlockSize);
  5175. {$ifndef FullDebugMode}
  5176. {$ifdef CheckHeapForCorruption}
  5177. {Check that this block is actually free and the next and previous blocks are both in use.}
  5178. if ((PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag))
  5179. or ((PNativeUInt(PByte(APointer) + (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and IsFreeBlockFlag) <> 0) then
  5180. begin
  5181. {$ifdef BCB6OrDelphi7AndUp}
  5182. System.Error(reInvalidPtr);
  5183. {$else}
  5184. System.RunError(reInvalidPtr);
  5185. {$endif}
  5186. end;
  5187. {$endif}
  5188. {$endif}
  5189. {Unlock medium blocks}
  5190. MediumBlocksLocked := False;
  5191. {All OK}
  5192. Result := 0;
  5193. {$ifndef FullDebugMode}
  5194. end
  5195. else
  5196. begin
  5197. {Should this become the new sequential feed?}
  5198. if MediumSequentialFeedBytesLeft <> MediumBlockPoolSize - MediumBlockPoolHeaderSize then
  5199. begin
  5200. {Bin the current sequential feed}
  5201. BinMediumSequentialFeedRemainder;
  5202. {Set this medium pool up as the new sequential feed pool:
  5203. Store the sequential feed pool trailer}
  5204. PNativeUInt(PByte(APointer) + LBlockSize - BlockHeaderSize)^ := IsMediumBlockFlag;
  5205. {Store the number of bytes available in the sequential feed chunk}
  5206. MediumSequentialFeedBytesLeft := MediumBlockPoolSize - MediumBlockPoolHeaderSize;
  5207. {Set the last sequentially fed block}
  5208. LastSequentiallyFedMediumBlock := Pointer(PByte(APointer) + LBlockSize);
  5209. {Unlock medium blocks}
  5210. MediumBlocksLocked := False;
  5211. {Success}
  5212. Result := 0;
  5213. end
  5214. else
  5215. begin
  5216. {Remove this medium block pool from the linked list}
  5217. Dec(PByte(APointer), MediumBlockPoolHeaderSize);
  5218. LPPreviousMediumBlockPoolHeader := PMediumBlockPoolHeader(APointer).PreviousMediumBlockPoolHeader;
  5219. LPNextMediumBlockPoolHeader := PMediumBlockPoolHeader(APointer).NextMediumBlockPoolHeader;
  5220. LPPreviousMediumBlockPoolHeader.NextMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
  5221. LPNextMediumBlockPoolHeader.PreviousMediumBlockPoolHeader := LPPreviousMediumBlockPoolHeader;
  5222. {Unlock medium blocks}
  5223. MediumBlocksLocked := False;
  5224. {$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
  5225. FillChar(APointer^, MediumBlockPoolSize, 0);
  5226. {$endif}
  5227. {Free the medium block pool}
  5228. if VirtualFree(APointer, 0, MEM_RELEASE) then
  5229. Result := 0
  5230. else
  5231. Result := -1;
  5232. end;
  5233. end;
  5234. {$endif}
  5235. end;
  5236. {$endif}
  5237. {Replacement for SysFreeMem}
  5238. function FastFreeMem(APointer: Pointer): Integer;
  5239. {$ifndef ASMVersion}
  5240. var
  5241. LPSmallBlockPool{$ifndef FullDebugMode}, LPPreviousPool, LPNextPool{$endif},
  5242. LPOldFirstPool: PSmallBlockPoolHeader;
  5243. LPSmallBlockType: PSmallBlockType;
  5244. LOldFirstFreeBlock: Pointer;
  5245. LBlockHeader: NativeUInt;
  5246. begin
  5247. {Get the small block header: Is it actually a small block?}
  5248. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  5249. {Is it a small block that is in use?}
  5250. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
  5251. begin
  5252. {Get a pointer to the block pool}
  5253. LPSmallBlockPool := PSmallBlockPoolHeader(LBlockHeader);
  5254. {Get the block type}
  5255. LPSmallBlockType := LPSmallBlockPool.BlockType;
  5256. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5257. FillChar(APointer^, LPSmallBlockType.BlockSize - BlockHeaderSize, 0);
  5258. {$endif}
  5259. {Lock the block type}
  5260. {$ifndef AssumeMultiThreaded}
  5261. if IsMultiThread then
  5262. {$endif}
  5263. begin
  5264. while (LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) <> 0) do
  5265. begin
  5266. {$ifdef NeverSleepOnThreadContention}
  5267. {$ifdef UseSwitchToThread}
  5268. SwitchToThread;
  5269. {$endif}
  5270. {$else}
  5271. Sleep(InitialSleepTime);
  5272. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  5273. Break;
  5274. Sleep(AdditionalSleepTime);
  5275. {$endif}
  5276. end;
  5277. end;
  5278. {Get the old first free block}
  5279. LOldFirstFreeBlock := LPSmallBlockPool.FirstFreeBlock;
  5280. {Was the pool manager previously full?}
  5281. if LOldFirstFreeBlock = nil then
  5282. begin
  5283. {Insert this as the first partially free pool for the block size}
  5284. LPOldFirstPool := LPSmallBlockType.NextPartiallyFreePool;
  5285. LPSmallBlockPool.NextPartiallyFreePool := LPOldFirstPool;
  5286. LPOldFirstPool.PreviousPartiallyFreePool := LPSmallBlockPool;
  5287. LPSmallBlockPool.PreviousPartiallyFreePool := PSmallBlockPoolHeader(LPSmallBlockType);
  5288. LPSmallBlockType.NextPartiallyFreePool := LPSmallBlockPool;
  5289. end;
  5290. {Store the old first free block}
  5291. PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := UIntPtr(LOldFirstFreeBlock) or IsFreeBlockFlag;
  5292. {Store this as the new first free block}
  5293. LPSmallBlockPool.FirstFreeBlock := APointer;
  5294. {Decrement the number of allocated blocks}
  5295. Dec(LPSmallBlockPool.BlocksInUse);
  5296. {Small block pools are never freed in full debug mode. This increases the
  5297. likehood of success in catching objects still being used after being
  5298. destroyed.}
  5299. {$ifndef FullDebugMode}
  5300. {Is the entire pool now free? -> Free it.}
  5301. if LPSmallBlockPool.BlocksInUse = 0 then
  5302. begin
  5303. {Get the previous and next chunk managers}
  5304. LPPreviousPool := LPSmallBlockPool.PreviousPartiallyFreePool;
  5305. LPNextPool := LPSmallBlockPool.NextPartiallyFreePool;
  5306. {Remove this manager}
  5307. LPPreviousPool.NextPartiallyFreePool := LPNextPool;
  5308. LPNextPool.PreviousPartiallyFreePool := LPPreviousPool;
  5309. {Is this the sequential feed pool? If so, stop sequential feeding}
  5310. if (LPSmallBlockType.CurrentSequentialFeedPool = LPSmallBlockPool) then
  5311. LPSmallBlockType.MaxSequentialFeedBlockAddress := nil;
  5312. {Unlock this block type}
  5313. LPSmallBlockType.BlockTypeLocked := False;
  5314. {Free the block pool}
  5315. FreeMediumBlock(LPSmallBlockPool);
  5316. end
  5317. else
  5318. begin
  5319. {$endif}
  5320. {Unlock this block type}
  5321. LPSmallBlockType.BlockTypeLocked := False;
  5322. {$ifndef FullDebugMode}
  5323. end;
  5324. {$endif}
  5325. {No error}
  5326. Result := 0;
  5327. end
  5328. else
  5329. begin
  5330. {Is this a medium block or a large block?}
  5331. if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
  5332. begin
  5333. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5334. {Get the block header, extract the block size and clear the block it.}
  5335. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  5336. FillChar(APointer^,
  5337. (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize, 0);
  5338. {$endif}
  5339. Result := FreeMediumBlock(APointer);
  5340. end
  5341. else
  5342. begin
  5343. {Validate: Is this actually a Large block, or is it an attempt to free an
  5344. already freed small block?}
  5345. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
  5346. Result := FreeLargeBlock(APointer)
  5347. else
  5348. Result := -1;
  5349. end;
  5350. end;
  5351. end;
  5352. {$else}
  5353. {$ifdef 32Bit}
  5354. asm
  5355. {Get the block header in edx}
  5356. mov edx, [eax - 4]
  5357. {Is it a small block in use?}
  5358. test dl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
  5359. {Save the pointer in ecx}
  5360. mov ecx, eax
  5361. {Save ebx}
  5362. push ebx
  5363. {Get the IsMultiThread variable in bl}
  5364. {$ifndef AssumeMultiThreaded}
  5365. mov bl, IsMultiThread
  5366. {$endif}
  5367. {Is it a small block that is in use?}
  5368. jnz @NotSmallBlockInUse
  5369. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5370. push edx
  5371. push ecx
  5372. mov edx, TSmallBlockPoolHeader[edx].BlockType
  5373. movzx edx, TSmallBlockType(edx).BlockSize
  5374. sub edx, BlockHeaderSize
  5375. xor ecx, ecx
  5376. call System.@FillChar
  5377. pop ecx
  5378. pop edx
  5379. {$endif}
  5380. {Do we need to lock the block type?}
  5381. {$ifndef AssumeMultiThreaded}
  5382. test bl, bl
  5383. {$endif}
  5384. {Get the small block type in ebx}
  5385. mov ebx, TSmallBlockPoolHeader[edx].BlockType
  5386. {Do we need to lock the block type?}
  5387. {$ifndef AssumeMultiThreaded}
  5388. jnz @LockBlockTypeLoop
  5389. {$else}
  5390. jmp @LockBlockTypeLoop
  5391. {Align branch target}
  5392. nop
  5393. {$endif}
  5394. @GotLockOnSmallBlockType:
  5395. {Current state: edx = @SmallBlockPoolHeader, ecx = APointer, ebx = @SmallBlockType}
  5396. {Decrement the number of blocks in use}
  5397. sub TSmallBlockPoolHeader[edx].BlocksInUse, 1
  5398. {Get the old first free block}
  5399. mov eax, TSmallBlockPoolHeader[edx].FirstFreeBlock
  5400. {Is the pool now empty?}
  5401. jz @PoolIsNowEmpty
  5402. {Was the pool full?}
  5403. test eax, eax
  5404. {Store this as the new first free block}
  5405. mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
  5406. {Store the previous first free block as the block header}
  5407. lea eax, [eax + IsFreeBlockFlag]
  5408. mov [ecx - 4], eax
  5409. {Insert the pool back into the linked list if it was full}
  5410. jz @SmallPoolWasFull
  5411. {All ok}
  5412. xor eax, eax
  5413. {Unlock the block type}
  5414. mov TSmallBlockType[ebx].BlockTypeLocked, al
  5415. {Restore registers}
  5416. pop ebx
  5417. {Done}
  5418. ret
  5419. {Align branch target}
  5420. {$ifndef AssumeMultiThreaded}
  5421. nop
  5422. {$endif}
  5423. @SmallPoolWasFull:
  5424. {Insert this as the first partially free pool for the block size}
  5425. mov ecx, TSmallBlockType[ebx].NextPartiallyFreePool
  5426. mov TSmallBlockPoolHeader[edx].PreviousPartiallyFreePool, ebx
  5427. mov TSmallBlockPoolHeader[edx].NextPartiallyFreePool, ecx
  5428. mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, edx
  5429. mov TSmallBlockType[ebx].NextPartiallyFreePool, edx
  5430. {Unlock the block type}
  5431. mov TSmallBlockType[ebx].BlockTypeLocked, False
  5432. {All ok}
  5433. xor eax, eax
  5434. {Restore registers}
  5435. pop ebx
  5436. {Done}
  5437. ret
  5438. {Align branch target}
  5439. nop
  5440. nop
  5441. @PoolIsNowEmpty:
  5442. {Was this pool actually in the linked list of pools with space? If not, it
  5443. can only be the sequential feed pool (it is the only pool that may contain
  5444. only one block, i.e. other blocks have not been split off yet)}
  5445. test eax, eax
  5446. jz @IsSequentialFeedPool
  5447. {Pool is now empty: Remove it from the linked list and free it}
  5448. mov eax, TSmallBlockPoolHeader[edx].PreviousPartiallyFreePool
  5449. mov ecx, TSmallBlockPoolHeader[edx].NextPartiallyFreePool
  5450. {Remove this manager}
  5451. mov TSmallBlockPoolHeader[eax].NextPartiallyFreePool, ecx
  5452. mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, eax
  5453. {Zero out eax}
  5454. xor eax, eax
  5455. {Is this the sequential feed pool? If so, stop sequential feeding}
  5456. cmp TSmallBlockType[ebx].CurrentSequentialFeedPool, edx
  5457. jne @NotSequentialFeedPool
  5458. @IsSequentialFeedPool:
  5459. mov TSmallBlockType[ebx].MaxSequentialFeedBlockAddress, eax
  5460. @NotSequentialFeedPool:
  5461. {Unlock the block type}
  5462. mov TSmallBlockType[ebx].BlockTypeLocked, al
  5463. {Release this pool}
  5464. mov eax, edx
  5465. mov edx, [edx - 4]
  5466. {$ifndef AssumeMultiThreaded}
  5467. mov bl, IsMultiThread
  5468. {$endif}
  5469. jmp @FreeMediumBlock
  5470. {Align branch target}
  5471. {$ifndef AssumeMultiThreaded}
  5472. nop
  5473. nop
  5474. {$endif}
  5475. nop
  5476. @LockBlockTypeLoop:
  5477. mov eax, $100
  5478. {Attempt to grab the block type}
  5479. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  5480. je @GotLockOnSmallBlockType
  5481. {$ifdef NeverSleepOnThreadContention}
  5482. {Pause instruction (improves performance on P4)}
  5483. rep nop
  5484. {$ifdef UseSwitchToThread}
  5485. push ecx
  5486. push edx
  5487. call SwitchToThread
  5488. pop edx
  5489. pop ecx
  5490. {$endif}
  5491. {Try again}
  5492. jmp @LockBlockTypeLoop
  5493. {Align branch target}
  5494. {$ifndef UseSwitchToThread}
  5495. nop
  5496. {$endif}
  5497. {$else}
  5498. {Couldn't grab the block type - sleep and try again}
  5499. push ecx
  5500. push edx
  5501. push InitialSleepTime
  5502. call Sleep
  5503. pop edx
  5504. pop ecx
  5505. {Try again}
  5506. mov eax, $100
  5507. {Attempt to grab the block type}
  5508. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  5509. je @GotLockOnSmallBlockType
  5510. {Couldn't grab the block type - sleep and try again}
  5511. push ecx
  5512. push edx
  5513. push AdditionalSleepTime
  5514. call Sleep
  5515. pop edx
  5516. pop ecx
  5517. {Try again}
  5518. jmp @LockBlockTypeLoop
  5519. {Align branch target}
  5520. nop
  5521. nop
  5522. {$endif}
  5523. {---------------------Medium blocks------------------------------}
  5524. {Align branch target}
  5525. @NotSmallBlockInUse:
  5526. {Not a small block in use: is it a medium or large block?}
  5527. test dl, IsFreeBlockFlag + IsLargeBlockFlag
  5528. jnz @NotASmallOrMediumBlock
  5529. @FreeMediumBlock:
  5530. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5531. push eax
  5532. push edx
  5533. and edx, DropMediumAndLargeFlagsMask
  5534. sub edx, BlockHeaderSize
  5535. xor ecx, ecx
  5536. call System.@FillChar
  5537. pop edx
  5538. pop eax
  5539. {$endif}
  5540. {Drop the flags}
  5541. and edx, DropMediumAndLargeFlagsMask
  5542. {Free the medium block pointed to by eax, header in edx, bl = IsMultiThread}
  5543. {$ifndef AssumeMultiThreaded}
  5544. {Do we need to lock the medium blocks?}
  5545. test bl, bl
  5546. {$endif}
  5547. {Block size in ebx}
  5548. mov ebx, edx
  5549. {Save registers}
  5550. push esi
  5551. {Pointer in esi}
  5552. mov esi, eax
  5553. {Do we need to lock the medium blocks?}
  5554. {$ifndef AssumeMultiThreaded}
  5555. jz @MediumBlocksLocked
  5556. {$endif}
  5557. call LockMediumBlocks
  5558. @MediumBlocksLocked:
  5559. {Can we combine this block with the next free block?}
  5560. test dword ptr [esi + ebx - 4], IsFreeBlockFlag
  5561. {Get the next block size and flags in ecx}
  5562. mov ecx, [esi + ebx - 4]
  5563. jnz @NextBlockIsFree
  5564. {Set the "PreviousIsFree" flag in the next block}
  5565. or ecx, PreviousMediumBlockIsFreeFlag
  5566. mov [esi + ebx - 4], ecx
  5567. @NextBlockChecked:
  5568. {Can we combine this block with the previous free block? We need to
  5569. re-read the flags since it could have changed before we could lock the
  5570. medium blocks.}
  5571. test byte ptr [esi - 4], PreviousMediumBlockIsFreeFlag
  5572. jnz @PreviousBlockIsFree
  5573. @PreviousBlockChecked:
  5574. {Is the entire medium block pool free, and there are other free blocks
  5575. that can fit the largest possible medium block -> free it.}
  5576. cmp ebx, (MediumBlockPoolSize - MediumBlockPoolHeaderSize)
  5577. je @EntireMediumPoolFree
  5578. @BinFreeMediumBlock:
  5579. {Store the size of the block as well as the flags}
  5580. lea eax, [ebx + IsMediumBlockFlag + IsFreeBlockFlag]
  5581. mov [esi - 4], eax
  5582. {Store the trailing size marker}
  5583. mov [esi + ebx - 8], ebx
  5584. {Insert this block back into the bins: Size check not required here,
  5585. since medium blocks that are in use are not allowed to be
  5586. shrunk smaller than MinimumMediumBlockSize}
  5587. mov eax, esi
  5588. mov edx, ebx
  5589. {Insert into bin}
  5590. call InsertMediumBlockIntoBin
  5591. {Unlock medium blocks}
  5592. mov MediumBlocksLocked, False;
  5593. {All OK}
  5594. xor eax, eax
  5595. {Restore registers}
  5596. pop esi
  5597. pop ebx
  5598. {Return}
  5599. ret
  5600. {Align branch target}
  5601. @NextBlockIsFree:
  5602. {Get the next block address in eax}
  5603. lea eax, [esi + ebx]
  5604. {Increase the size of this block}
  5605. and ecx, DropMediumAndLargeFlagsMask
  5606. add ebx, ecx
  5607. {Was the block binned?}
  5608. cmp ecx, MinimumMediumBlockSize
  5609. jb @NextBlockChecked
  5610. call RemoveMediumFreeBlock
  5611. jmp @NextBlockChecked
  5612. {Align branch target}
  5613. nop
  5614. @PreviousBlockIsFree:
  5615. {Get the size of the free block just before this one}
  5616. mov ecx, [esi - 8]
  5617. {Include the previous block}
  5618. sub esi, ecx
  5619. {Set the new block size}
  5620. add ebx, ecx
  5621. {Remove the previous block from the linked list}
  5622. cmp ecx, MinimumMediumBlockSize
  5623. jb @PreviousBlockChecked
  5624. mov eax, esi
  5625. call RemoveMediumFreeBlock
  5626. jmp @PreviousBlockChecked
  5627. {Align branch target}
  5628. @EntireMediumPoolFree:
  5629. {Should we make this the new sequential feed medium block pool? If the
  5630. current sequential feed pool is not entirely free, we make this the new
  5631. sequential feed pool.}
  5632. cmp MediumSequentialFeedBytesLeft, MediumBlockPoolSize - MediumBlockPoolHeaderSize
  5633. jne @MakeEmptyMediumPoolSequentialFeed
  5634. {Point esi to the medium block pool header}
  5635. sub esi, MediumBlockPoolHeaderSize
  5636. {Remove this medium block pool from the linked list}
  5637. mov eax, TMediumBlockPoolHeader[esi].PreviousMediumBlockPoolHeader
  5638. mov edx, TMediumBlockPoolHeader[esi].NextMediumBlockPoolHeader
  5639. mov TMediumBlockPoolHeader[eax].NextMediumBlockPoolHeader, edx
  5640. mov TMediumBlockPoolHeader[edx].PreviousMediumBlockPoolHeader, eax
  5641. {Unlock medium blocks}
  5642. mov MediumBlocksLocked, False;
  5643. {$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
  5644. mov eax, esi
  5645. mov edx, MediumBlockPoolSize
  5646. xor ecx, ecx
  5647. call System.@FillChar
  5648. {$endif}
  5649. {Free the medium block pool}
  5650. push MEM_RELEASE
  5651. push 0
  5652. push esi
  5653. call VirtualFree
  5654. {VirtualFree returns >0 if all is ok}
  5655. cmp eax, 1
  5656. {Return 0 on all ok}
  5657. sbb eax, eax
  5658. {Restore registers}
  5659. pop esi
  5660. pop ebx
  5661. ret
  5662. {Align branch target}
  5663. nop
  5664. nop
  5665. nop
  5666. @MakeEmptyMediumPoolSequentialFeed:
  5667. {Get a pointer to the end-marker block}
  5668. lea ebx, [esi + MediumBlockPoolSize - MediumBlockPoolHeaderSize]
  5669. {Bin the current sequential feed pool}
  5670. call BinMediumSequentialFeedRemainder
  5671. {Set this medium pool up as the new sequential feed pool:
  5672. Store the sequential feed pool trailer}
  5673. mov dword ptr [ebx - BlockHeaderSize], IsMediumBlockFlag
  5674. {Store the number of bytes available in the sequential feed chunk}
  5675. mov MediumSequentialFeedBytesLeft, MediumBlockPoolSize - MediumBlockPoolHeaderSize
  5676. {Set the last sequentially fed block}
  5677. mov LastSequentiallyFedMediumBlock, ebx
  5678. {Unlock medium blocks}
  5679. mov MediumBlocksLocked, False;
  5680. {Success}
  5681. xor eax, eax
  5682. {Restore registers}
  5683. pop esi
  5684. pop ebx
  5685. ret
  5686. {Align branch target}
  5687. nop
  5688. nop
  5689. @NotASmallOrMediumBlock:
  5690. {Restore ebx}
  5691. pop ebx
  5692. {Is it in fact a large block?}
  5693. test dl, IsFreeBlockFlag + IsMediumBlockFlag
  5694. jz FreeLargeBlock
  5695. {Attempt to free an already free block}
  5696. mov eax, -1
  5697. end;
  5698. {$else}
  5699. {---------------64-bit BASM FastFreeMem---------------}
  5700. asm
  5701. .params 3
  5702. .pushnv rbx
  5703. .pushnv rsi
  5704. {Get the block header in rdx}
  5705. mov rdx, [rcx - BlockHeaderSize]
  5706. {Is it a small block in use?}
  5707. test dl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
  5708. {Get the IsMultiThread variable in bl}
  5709. {$ifndef AssumeMultiThreaded}
  5710. mov bl, IsMultiThread
  5711. {$endif}
  5712. {Is it a small block that is in use?}
  5713. jnz @NotSmallBlockInUse
  5714. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5715. mov rsi, rcx
  5716. mov rdx, TSmallBlockPoolHeader[rdx].BlockType
  5717. movzx edx, TSmallBlockType(rdx).BlockSize
  5718. sub edx, BlockHeaderSize
  5719. xor r8, r8
  5720. call System.@FillChar
  5721. mov rcx, rsi
  5722. mov rdx, [rcx - BlockHeaderSize]
  5723. {$endif}
  5724. {Do we need to lock the block type?}
  5725. {$ifndef AssumeMultiThreaded}
  5726. test bl, bl
  5727. {$endif}
  5728. {Get the small block type in rbx}
  5729. mov rbx, TSmallBlockPoolHeader[rdx].BlockType
  5730. {Do we need to lock the block type?}
  5731. {$ifndef AssumeMultiThreaded}
  5732. jnz @LockBlockTypeLoop
  5733. {$else}
  5734. jmp @LockBlockTypeLoop
  5735. {$endif}
  5736. @GotLockOnSmallBlockType:
  5737. {Current state: rdx = @SmallBlockPoolHeader, rcx = APointer, rbx = @SmallBlockType}
  5738. {Decrement the number of blocks in use}
  5739. sub TSmallBlockPoolHeader[rdx].BlocksInUse, 1
  5740. {Get the old first free block}
  5741. mov rax, TSmallBlockPoolHeader[rdx].FirstFreeBlock
  5742. {Is the pool now empty?}
  5743. jz @PoolIsNowEmpty
  5744. {Was the pool full?}
  5745. test rax, rax
  5746. {Store this as the new first free block}
  5747. mov TSmallBlockPoolHeader[rdx].FirstFreeBlock, rcx
  5748. {Store the previous first free block as the block header}
  5749. lea rax, [rax + IsFreeBlockFlag]
  5750. mov [rcx - BlockHeaderSize], rax
  5751. {Insert the pool back into the linked list if it was full}
  5752. jz @SmallPoolWasFull
  5753. {All ok}
  5754. xor eax, eax
  5755. {Unlock the block type}
  5756. mov TSmallBlockType[rbx].BlockTypeLocked, al
  5757. jmp @Done
  5758. @SmallPoolWasFull:
  5759. {Insert this as the first partially free pool for the block size}
  5760. mov rcx, TSmallBlockType[rbx].NextPartiallyFreePool
  5761. mov TSmallBlockPoolHeader[rdx].PreviousPartiallyFreePool, rbx
  5762. mov TSmallBlockPoolHeader[rdx].NextPartiallyFreePool, rcx
  5763. mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rdx
  5764. mov TSmallBlockType[rbx].NextPartiallyFreePool, rdx
  5765. {Unlock the block type}
  5766. mov TSmallBlockType[rbx].BlockTypeLocked, False
  5767. {All ok}
  5768. xor eax, eax
  5769. jmp @Done
  5770. @PoolIsNowEmpty:
  5771. {Was this pool actually in the linked list of pools with space? If not, it
  5772. can only be the sequential feed pool (it is the only pool that may contain
  5773. only one block, i.e. other blocks have not been split off yet)}
  5774. test rax, rax
  5775. jz @IsSequentialFeedPool
  5776. {Pool is now empty: Remove it from the linked list and free it}
  5777. mov rax, TSmallBlockPoolHeader[rdx].PreviousPartiallyFreePool
  5778. mov rcx, TSmallBlockPoolHeader[rdx].NextPartiallyFreePool
  5779. {Remove this manager}
  5780. mov TSmallBlockPoolHeader[rax].NextPartiallyFreePool, rcx
  5781. mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rax
  5782. {Zero out eax}
  5783. xor rax, rax
  5784. {Is this the sequential feed pool? If so, stop sequential feeding}
  5785. cmp TSmallBlockType[rbx].CurrentSequentialFeedPool, rdx
  5786. jne @NotSequentialFeedPool
  5787. @IsSequentialFeedPool:
  5788. mov TSmallBlockType[rbx].MaxSequentialFeedBlockAddress, rax
  5789. @NotSequentialFeedPool:
  5790. {Unlock the block type}
  5791. mov TSmallBlockType[rbx].BlockTypeLocked, al
  5792. {Release this pool}
  5793. mov rcx, rdx
  5794. mov rdx, [rdx - BlockHeaderSize]
  5795. {$ifndef AssumeMultiThreaded}
  5796. mov bl, IsMultiThread
  5797. {$endif}
  5798. jmp @FreeMediumBlock
  5799. @LockBlockTypeLoop:
  5800. mov eax, $100
  5801. {Attempt to grab the block type}
  5802. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  5803. je @GotLockOnSmallBlockType
  5804. {$ifdef NeverSleepOnThreadContention}
  5805. {Pause instruction (improves performance on P4)}
  5806. pause
  5807. {$ifdef UseSwitchToThread}
  5808. mov rsi, rcx
  5809. call SwitchToThread
  5810. mov rcx, rsi
  5811. mov rdx, [rcx - BlockHeaderSize]
  5812. {$endif}
  5813. {Try again}
  5814. jmp @LockBlockTypeLoop
  5815. {$else}
  5816. {Couldn't grab the block type - sleep and try again}
  5817. mov rsi, rcx
  5818. mov ecx, InitialSleepTime
  5819. call Sleep
  5820. mov rcx, rsi
  5821. mov rdx, [rcx - BlockHeaderSize]
  5822. {Try again}
  5823. mov eax, $100
  5824. {Attempt to grab the block type}
  5825. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  5826. je @GotLockOnSmallBlockType
  5827. {Couldn't grab the block type - sleep and try again}
  5828. mov rsi, rcx
  5829. mov ecx, AdditionalSleepTime
  5830. call Sleep
  5831. mov rcx, rsi
  5832. mov rdx, [rcx - BlockHeaderSize]
  5833. {Try again}
  5834. jmp @LockBlockTypeLoop
  5835. {$endif}
  5836. {---------------------Medium blocks------------------------------}
  5837. @NotSmallBlockInUse:
  5838. {Not a small block in use: is it a medium or large block?}
  5839. test dl, IsFreeBlockFlag + IsLargeBlockFlag
  5840. jnz @NotASmallOrMediumBlock
  5841. @FreeMediumBlock:
  5842. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5843. mov rsi, rcx
  5844. and rdx, DropMediumAndLargeFlagsMask
  5845. sub rdx, BlockHeaderSize
  5846. xor r8, r8
  5847. call System.@FillChar
  5848. mov rcx, rsi
  5849. mov rdx, [rcx - BlockHeaderSize]
  5850. {$endif}
  5851. {Drop the flags}
  5852. and rdx, DropMediumAndLargeFlagsMask
  5853. {Free the medium block pointed to by eax, header in edx, bl = IsMultiThread}
  5854. {$ifndef AssumeMultiThreaded}
  5855. {Do we need to lock the medium blocks?}
  5856. test bl, bl
  5857. {$endif}
  5858. {Block size in rbx}
  5859. mov rbx, rdx
  5860. {Pointer in rsi}
  5861. mov rsi, rcx
  5862. {Do we need to lock the medium blocks?}
  5863. {$ifndef AssumeMultiThreaded}
  5864. jz @MediumBlocksLocked
  5865. {$endif}
  5866. call LockMediumBlocks
  5867. @MediumBlocksLocked:
  5868. {Can we combine this block with the next free block?}
  5869. test qword ptr [rsi + rbx - BlockHeaderSize], IsFreeBlockFlag
  5870. {Get the next block size and flags in rcx}
  5871. mov rcx, [rsi + rbx - BlockHeaderSize]
  5872. jnz @NextBlockIsFree
  5873. {Set the "PreviousIsFree" flag in the next block}
  5874. or rcx, PreviousMediumBlockIsFreeFlag
  5875. mov [rsi + rbx - BlockHeaderSize], rcx
  5876. @NextBlockChecked:
  5877. {Can we combine this block with the previous free block? We need to
  5878. re-read the flags since it could have changed before we could lock the
  5879. medium blocks.}
  5880. test byte ptr [esi - BlockHeaderSize], PreviousMediumBlockIsFreeFlag
  5881. jnz @PreviousBlockIsFree
  5882. @PreviousBlockChecked:
  5883. {Is the entire medium block pool free, and there are other free blocks
  5884. that can fit the largest possible medium block -> free it.}
  5885. cmp ebx, (MediumBlockPoolSize - MediumBlockPoolHeaderSize)
  5886. je @EntireMediumPoolFree
  5887. @BinFreeMediumBlock:
  5888. {Store the size of the block as well as the flags}
  5889. lea rax, [rbx + IsMediumBlockFlag + IsFreeBlockFlag]
  5890. mov [rsi - BlockHeaderSize], rax
  5891. {Store the trailing size marker}
  5892. mov [rsi + rbx - 2 * BlockHeaderSize], rbx
  5893. {Insert this block back into the bins: Size check not required here,
  5894. since medium blocks that are in use are not allowed to be
  5895. shrunk smaller than MinimumMediumBlockSize}
  5896. mov rcx, rsi
  5897. mov rdx, rbx
  5898. {Insert into bin}
  5899. call InsertMediumBlockIntoBin
  5900. {All OK}
  5901. xor eax, eax
  5902. {Unlock medium blocks}
  5903. mov MediumBlocksLocked, al
  5904. jmp @Done
  5905. @NextBlockIsFree:
  5906. {Get the next block address in rax}
  5907. lea rax, [rsi + rbx]
  5908. {Increase the size of this block}
  5909. and rcx, DropMediumAndLargeFlagsMask
  5910. add rbx, rcx
  5911. {Was the block binned?}
  5912. cmp rcx, MinimumMediumBlockSize
  5913. jb @NextBlockChecked
  5914. mov rcx, rax
  5915. call RemoveMediumFreeBlock
  5916. jmp @NextBlockChecked
  5917. @PreviousBlockIsFree:
  5918. {Get the size of the free block just before this one}
  5919. mov rcx, [rsi - 2 * BlockHeaderSize]
  5920. {Include the previous block}
  5921. sub rsi, rcx
  5922. {Set the new block size}
  5923. add rbx, rcx
  5924. {Remove the previous block from the linked list}
  5925. cmp ecx, MinimumMediumBlockSize
  5926. jb @PreviousBlockChecked
  5927. mov rcx, rsi
  5928. call RemoveMediumFreeBlock
  5929. jmp @PreviousBlockChecked
  5930. @EntireMediumPoolFree:
  5931. {Should we make this the new sequential feed medium block pool? If the
  5932. current sequential feed pool is not entirely free, we make this the new
  5933. sequential feed pool.}
  5934. lea r8, MediumSequentialFeedBytesLeft
  5935. cmp dword ptr [r8], MediumBlockPoolSize - MediumBlockPoolHeaderSize //workaround for QC99023
  5936. jne @MakeEmptyMediumPoolSequentialFeed
  5937. {Point esi to the medium block pool header}
  5938. sub rsi, MediumBlockPoolHeaderSize
  5939. {Remove this medium block pool from the linked list}
  5940. mov rax, TMediumBlockPoolHeader[rsi].PreviousMediumBlockPoolHeader
  5941. mov rdx, TMediumBlockPoolHeader[rsi].NextMediumBlockPoolHeader
  5942. mov TMediumBlockPoolHeader[rax].NextMediumBlockPoolHeader, rdx
  5943. mov TMediumBlockPoolHeader[rdx].PreviousMediumBlockPoolHeader, rax
  5944. {Unlock medium blocks}
  5945. xor eax, eax
  5946. mov MediumBlocksLocked, al
  5947. {$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
  5948. mov rcx, rsi
  5949. mov edx, MediumBlockPoolSize
  5950. xor r8, r8
  5951. call System.@FillChar
  5952. {$endif}
  5953. {Free the medium block pool}
  5954. mov rcx, rsi
  5955. xor edx, edx
  5956. mov r8d, MEM_RELEASE
  5957. call VirtualFree
  5958. {VirtualFree returns >0 if all is ok}
  5959. cmp eax, 1
  5960. {Return 0 on all ok}
  5961. sbb eax, eax
  5962. jmp @Done
  5963. @MakeEmptyMediumPoolSequentialFeed:
  5964. {Get a pointer to the end-marker block}
  5965. lea rbx, [rsi + MediumBlockPoolSize - MediumBlockPoolHeaderSize]
  5966. {Bin the current sequential feed pool}
  5967. call BinMediumSequentialFeedRemainder
  5968. {Set this medium pool up as the new sequential feed pool:
  5969. Store the sequential feed pool trailer}
  5970. mov qword ptr [rbx - BlockHeaderSize], IsMediumBlockFlag
  5971. {Store the number of bytes available in the sequential feed chunk}
  5972. lea rax, MediumSequentialFeedBytesLeft
  5973. mov dword ptr [rax], MediumBlockPoolSize - MediumBlockPoolHeaderSize //QC99023 workaround
  5974. {Set the last sequentially fed block}
  5975. mov LastSequentiallyFedMediumBlock, rbx
  5976. {Success}
  5977. xor eax, eax
  5978. {Unlock medium blocks}
  5979. mov MediumBlocksLocked, al
  5980. jmp @Done
  5981. @NotASmallOrMediumBlock:
  5982. {Attempt to free an already free block?}
  5983. mov eax, -1
  5984. {Is it in fact a large block?}
  5985. test dl, IsFreeBlockFlag + IsMediumBlockFlag
  5986. jnz @Done
  5987. call FreeLargeBlock
  5988. @Done:
  5989. end;
  5990. {$endif}
  5991. {$endif}
  5992. {$ifndef FullDebugMode}
  5993. {Replacement for SysReallocMem}
  5994. function FastReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  5995. {$ifndef ASMVersion}
  5996. var
  5997. LBlockHeader, LNextBlockSizeAndFlags, LNewAllocSize, LBlockFlags,
  5998. LOldAvailableSize, LNextBlockSize, LNewAvailableSize, LMinimumUpsize,
  5999. LSecondSplitSize, LNewBlockSize: NativeUInt;
  6000. LPSmallBlockType: PSmallBlockType;
  6001. LPNextBlock, LPNextBlockHeader: Pointer;
  6002. {Upsizes a large block in-place. The following variables are assumed correct:
  6003. LBlockFlags, LOldAvailableSize, LPNextBlock, LNextBlockSizeAndFlags,
  6004. LNextBlockSize, LNewAvailableSize. Medium blocks must be locked on entry if
  6005. required.}
  6006. procedure MediumBlockInPlaceUpsize;
  6007. begin
  6008. {Remove the next block}
  6009. if LNextBlockSizeAndFlags >= MinimumMediumBlockSize then
  6010. RemoveMediumFreeBlock(LPNextBlock);
  6011. {Add 25% for medium block in-place upsizes}
  6012. LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
  6013. if NativeUInt(ANewSize) < LMinimumUpsize then
  6014. LNewAllocSize := LMinimumUpsize
  6015. else
  6016. LNewAllocSize := NativeUInt(ANewSize);
  6017. {Round up to the nearest block size granularity}
  6018. LNewBlockSize := ((LNewAllocSize + (BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset))
  6019. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  6020. {Calculate the size of the second split}
  6021. LSecondSplitSize := LNewAvailableSize + BlockHeaderSize - LNewBlockSize;
  6022. {Does it fit?}
  6023. if NativeInt(LSecondSplitSize) <= 0 then
  6024. begin
  6025. {The block size is the full available size plus header}
  6026. LNewBlockSize := LNewAvailableSize + BlockHeaderSize;
  6027. {Grab the whole block: Mark it as used in the block following it}
  6028. LPNextBlockHeader := Pointer(PByte(APointer) + LNewAvailableSize);
  6029. PNativeUInt(LPNextBlockHeader)^ :=
  6030. PNativeUInt(LPNextBlockHeader)^ and (not PreviousMediumBlockIsFreeFlag);
  6031. end
  6032. else
  6033. begin
  6034. {Split the block in two}
  6035. LPNextBlock := PMediumFreeBlock(PByte(APointer) + LNewBlockSize);
  6036. {Set the size of the second split}
  6037. PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  6038. {Store the size of the second split before the header of the next block}
  6039. PNativeUInt(PByte(LPNextBlock) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
  6040. {Put the remainder in a bin if it is big enough}
  6041. if LSecondSplitSize >= MinimumMediumBlockSize then
  6042. InsertMediumBlockIntoBin(LPNextBlock, LSecondSplitSize);
  6043. end;
  6044. {Set the size and flags for this block}
  6045. PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := LNewBlockSize or LBlockFlags;
  6046. end;
  6047. {In-place downsize of a medium block. On entry Size must be less than half of
  6048. LOldAvailableSize.}
  6049. procedure MediumBlockInPlaceDownsize;
  6050. begin
  6051. {Round up to the next medium block size}
  6052. LNewBlockSize := ((ANewSize + (BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset))
  6053. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  6054. {Get the size of the second split}
  6055. LSecondSplitSize := (LOldAvailableSize + BlockHeaderSize) - LNewBlockSize;
  6056. {Lock the medium blocks}
  6057. LockMediumBlocks;
  6058. {Set the new size}
  6059. PNativeUInt(PByte(APointer) - BlockHeaderSize)^ :=
  6060. (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask)
  6061. or LNewBlockSize;
  6062. {Is the next block in use?}
  6063. LPNextBlock := PNativeUInt(PByte(APointer) + LOldAvailableSize + BlockHeaderSize);
  6064. LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
  6065. if LNextBlockSizeAndFlags and IsFreeBlockFlag = 0 then
  6066. begin
  6067. {The next block is in use: flag its previous block as free}
  6068. PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ :=
  6069. LNextBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
  6070. end
  6071. else
  6072. begin
  6073. {The next block is free: combine it}
  6074. LNextBlockSizeAndFlags := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  6075. Inc(LSecondSplitSize, LNextBlockSizeAndFlags);
  6076. if LNextBlockSizeAndFlags >= MinimumMediumBlockSize then
  6077. RemoveMediumFreeBlock(LPNextBlock);
  6078. end;
  6079. {Set the split}
  6080. LPNextBlock := PNativeUInt(PByte(APointer) + LNewBlockSize);
  6081. {Store the free part's header}
  6082. PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  6083. {Store the trailing size field}
  6084. PNativeUInt(PByte(LPNextBlock) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
  6085. {Bin this free block}
  6086. if LSecondSplitSize >= MinimumMediumBlockSize then
  6087. InsertMediumBlockIntoBin(LPNextBlock, LSecondSplitSize);
  6088. {Unlock the medium blocks}
  6089. MediumBlocksLocked := False;
  6090. end;
  6091. begin
  6092. {Get the block header: Is it actually a small block?}
  6093. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  6094. {Is it a small block that is in use?}
  6095. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
  6096. begin
  6097. {-----------------------------------Small block-------------------------------------}
  6098. {The block header is a pointer to the block pool: Get the block type}
  6099. LPSmallBlockType := PSmallBlockPoolHeader(LBlockHeader).BlockType;
  6100. {Get the available size inside blocks of this type.}
  6101. LOldAvailableSize := LPSmallBlockType.BlockSize - BlockHeaderSize;
  6102. {Is it an upsize or a downsize?}
  6103. if LOldAvailableSize >= NativeUInt(ANewSize) then
  6104. begin
  6105. {It's a downsize. Do we need to allocate a smaller block? Only if the new
  6106. block size is less than a quarter of the available size less
  6107. SmallBlockDownsizeCheckAdder bytes}
  6108. if (NativeUInt(ANewSize) * 4 + SmallBlockDownsizeCheckAdder) >= LOldAvailableSize then
  6109. begin
  6110. {In-place downsize - return the pointer}
  6111. Result := APointer;
  6112. Exit;
  6113. end
  6114. else
  6115. begin
  6116. {Allocate a smaller block}
  6117. Result := FastGetMem(ANewSize);
  6118. {Allocated OK?}
  6119. if Result <> nil then
  6120. begin
  6121. {Move the data across}
  6122. {$ifdef UseCustomVariableSizeMoveRoutines}
  6123. {$ifdef Align16Bytes}
  6124. MoveX16LP(APointer^, Result^, ANewSize);
  6125. {$else}
  6126. MoveX8LP(APointer^, Result^, ANewSize);
  6127. {$endif}
  6128. {$else}
  6129. System.Move(APointer^, Result^, ANewSize);
  6130. {$endif}
  6131. {Free the old pointer}
  6132. FastFreeMem(APointer);
  6133. end;
  6134. end;
  6135. end
  6136. else
  6137. begin
  6138. {This pointer is being reallocated to a larger block and therefore it is
  6139. logical to assume that it may be enlarged again. Since reallocations are
  6140. expensive, there is a minimum upsize percentage to avoid unnecessary
  6141. future move operations.}
  6142. {Must grow with at least 100% + x bytes}
  6143. LNewAllocSize := LOldAvailableSize * 2 + SmallBlockUpsizeAdder;
  6144. {Still not large enough?}
  6145. if LNewAllocSize < NativeUInt(ANewSize) then
  6146. LNewAllocSize := NativeUInt(ANewSize);
  6147. {Allocate the new block}
  6148. Result := FastGetMem(LNewAllocSize);
  6149. {Allocated OK?}
  6150. if Result <> nil then
  6151. begin
  6152. {Do we need to store the requested size? Only large blocks store the
  6153. requested size.}
  6154. if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
  6155. PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  6156. {Move the data across}
  6157. {$ifdef UseCustomFixedSizeMoveRoutines}
  6158. LPSmallBlockType.UpsizeMoveProcedure(APointer^, Result^, LOldAvailableSize);
  6159. {$else}
  6160. System.Move(APointer^, Result^, LOldAvailableSize);
  6161. {$endif}
  6162. {Free the old pointer}
  6163. FastFreeMem(APointer);
  6164. end;
  6165. end;
  6166. end
  6167. else
  6168. begin
  6169. {Is this a medium block or a large block?}
  6170. if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
  6171. begin
  6172. {-------------------------------Medium block--------------------------------------}
  6173. {What is the available size in the block being reallocated?}
  6174. LOldAvailableSize := (LBlockHeader and DropMediumAndLargeFlagsMask);
  6175. {Get a pointer to the next block}
  6176. LPNextBlock := PNativeUInt(PByte(APointer) + LOldAvailableSize);
  6177. {Subtract the block header size from the old available size}
  6178. Dec(LOldAvailableSize, BlockHeaderSize);
  6179. {Is it an upsize or a downsize?}
  6180. if NativeUInt(ANewSize) > LOldAvailableSize then
  6181. begin
  6182. {Can we do an in-place upsize?}
  6183. LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
  6184. {Is the next block free?}
  6185. if LNextBlockSizeAndFlags and IsFreeBlockFlag <> 0 then
  6186. begin
  6187. LNextBlockSize := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  6188. {The available size including the next block}
  6189. LNewAvailableSize := LOldAvailableSize + LNextBlockSize;
  6190. {Can the block fit?}
  6191. if NativeUInt(ANewSize) <= LNewAvailableSize then
  6192. begin
  6193. {The next block is free and there is enough space to grow this
  6194. block in place.}
  6195. {$ifndef AssumeMultiThreaded}
  6196. if IsMultiThread then
  6197. begin
  6198. {$endif}
  6199. {Multi-threaded application - lock medium blocks and re-read the
  6200. information on the blocks.}
  6201. LockMediumBlocks;
  6202. {Re-read the info for this block}
  6203. LBlockFlags := PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask;
  6204. {Re-read the info for the next block}
  6205. LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
  6206. {Recalculate the next block size}
  6207. LNextBlockSize := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  6208. {The available size including the next block}
  6209. LNewAvailableSize := LOldAvailableSize + LNextBlockSize;
  6210. {Is the next block still free and the size still sufficient?}
  6211. if (LNextBlockSizeAndFlags and IsFreeBlockFlag <> 0)
  6212. and (NativeUInt(ANewSize) <= LNewAvailableSize) then
  6213. begin
  6214. {Upsize the block in-place}
  6215. MediumBlockInPlaceUpsize;
  6216. {Unlock the medium blocks}
  6217. MediumBlocksLocked := False;
  6218. {Return the result}
  6219. Result := APointer;
  6220. {Done}
  6221. Exit;
  6222. end;
  6223. {Couldn't use the block: Unlock the medium blocks}
  6224. MediumBlocksLocked := False;
  6225. {$ifndef AssumeMultiThreaded}
  6226. end
  6227. else
  6228. begin
  6229. {Extract the block flags}
  6230. LBlockFlags := ExtractMediumAndLargeFlagsMask and LBlockHeader;
  6231. {Upsize the block in-place}
  6232. MediumBlockInPlaceUpsize;
  6233. {Return the result}
  6234. Result := APointer;
  6235. {Done}
  6236. Exit;
  6237. end;
  6238. {$endif}
  6239. end;
  6240. end;
  6241. {Couldn't upsize in place. Grab a new block and move the data across:
  6242. If we have to reallocate and move medium blocks, we grow by at
  6243. least 25%}
  6244. LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
  6245. if NativeUInt(ANewSize) < LMinimumUpsize then
  6246. LNewAllocSize := LMinimumUpsize
  6247. else
  6248. LNewAllocSize := NativeUInt(ANewSize);
  6249. {Allocate the new block}
  6250. Result := FastGetMem(LNewAllocSize);
  6251. if Result <> nil then
  6252. begin
  6253. {If it's a large block - store the actual user requested size}
  6254. if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
  6255. PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  6256. {Move the data across}
  6257. {$ifdef UseCustomVariableSizeMoveRoutines}
  6258. MoveX16LP(APointer^, Result^, LOldAvailableSize);
  6259. {$else}
  6260. System.Move(APointer^, Result^, LOldAvailableSize);
  6261. {$endif}
  6262. {Free the old block}
  6263. FastFreeMem(APointer);
  6264. end;
  6265. end
  6266. else
  6267. begin
  6268. {Must be less than half the current size or we don't bother resizing.}
  6269. if NativeUInt(ANewSize * 2) >= LOldAvailableSize then
  6270. begin
  6271. Result := APointer;
  6272. end
  6273. else
  6274. begin
  6275. {In-place downsize? Balance the cost of moving the data vs. the cost
  6276. of fragmenting the memory pool. Medium blocks in use may never be
  6277. smaller than MinimumMediumBlockSize.}
  6278. if NativeUInt(ANewSize) >= (MinimumMediumBlockSize - BlockHeaderSize) then
  6279. begin
  6280. MediumBlockInPlaceDownsize;
  6281. Result := APointer;
  6282. end
  6283. else
  6284. begin
  6285. {The requested size is less than the minimum medium block size. If
  6286. the requested size is less than the threshold value (currently a
  6287. quarter of the minimum medium block size), move the data to a small
  6288. block, otherwise shrink the medium block to the minimum allowable
  6289. medium block size.}
  6290. if NativeUInt(ANewSize) >= MediumInPlaceDownsizeLimit then
  6291. begin
  6292. {The request is for a size smaller than the minimum medium block
  6293. size, but not small enough to justify moving data: Reduce the
  6294. block size to the minimum medium block size}
  6295. ANewSize := MinimumMediumBlockSize - BlockHeaderSize;
  6296. {Is it already at the minimum medium block size?}
  6297. if LOldAvailableSize > NativeUInt(ANewSize) then
  6298. MediumBlockInPlaceDownsize;
  6299. Result := APointer;
  6300. end
  6301. else
  6302. begin
  6303. {Allocate the new block}
  6304. Result := FastGetMem(ANewSize);
  6305. if Result <> nil then
  6306. begin
  6307. {Move the data across}
  6308. {$ifdef UseCustomVariableSizeMoveRoutines}
  6309. {$ifdef Align16Bytes}
  6310. MoveX16LP(APointer^, Result^, ANewSize);
  6311. {$else}
  6312. MoveX8LP(APointer^, Result^, ANewSize);
  6313. {$endif}
  6314. {$else}
  6315. System.Move(APointer^, Result^, ANewSize);
  6316. {$endif}
  6317. {Free the old block}
  6318. FastFreeMem(APointer);
  6319. end;
  6320. end;
  6321. end;
  6322. end;
  6323. end;
  6324. end
  6325. else
  6326. begin
  6327. {Is this a valid large block?}
  6328. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
  6329. begin
  6330. {-----------------------Large block------------------------------}
  6331. Result := ReallocateLargeBlock(APointer, ANewSize);
  6332. end
  6333. else
  6334. begin
  6335. {-----------------------Invalid block------------------------------}
  6336. {Bad pointer: probably an attempt to reallocate a free memory block.}
  6337. Result := nil;
  6338. end;
  6339. end;
  6340. end;
  6341. end;
  6342. {$else}
  6343. {$ifdef 32Bit}
  6344. asm
  6345. {On entry: eax = APointer; edx = ANewSize}
  6346. {Get the block header: Is it actually a small block?}
  6347. mov ecx, [eax - 4]
  6348. {Is it a small block?}
  6349. test cl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
  6350. {Save ebx}
  6351. push ebx
  6352. {Save esi}
  6353. push esi
  6354. {Save the original pointer in esi}
  6355. mov esi, eax
  6356. {Is it a small block?}
  6357. jnz @NotASmallBlock
  6358. {-----------------------------------Small block-------------------------------------}
  6359. {Get the block type in ebx}
  6360. mov ebx, TSmallBlockPoolHeader[ecx].BlockType
  6361. {Get the available size inside blocks of this type.}
  6362. movzx ecx, TSmallBlockType[ebx].BlockSize
  6363. sub ecx, 4
  6364. {Is it an upsize or a downsize?}
  6365. cmp ecx, edx
  6366. jb @SmallUpsize
  6367. {It's a downsize. Do we need to allocate a smaller block? Only if the new
  6368. size is less than a quarter of the available size less
  6369. SmallBlockDownsizeCheckAdder bytes}
  6370. lea ebx, [edx * 4 + SmallBlockDownsizeCheckAdder]
  6371. cmp ebx, ecx
  6372. jb @NotSmallInPlaceDownsize
  6373. {In-place downsize - return the original pointer}
  6374. pop esi
  6375. pop ebx
  6376. ret
  6377. {Align branch target}
  6378. nop
  6379. @NotSmallInPlaceDownsize:
  6380. {Save the requested size}
  6381. mov ebx, edx
  6382. {Allocate a smaller block}
  6383. mov eax, edx
  6384. call FastGetMem
  6385. {Allocated OK?}
  6386. test eax, eax
  6387. jz @SmallDownsizeDone
  6388. {Move data across: count in ecx}
  6389. mov ecx, ebx
  6390. {Destination in edx}
  6391. mov edx, eax
  6392. {Save the result in ebx}
  6393. mov ebx, eax
  6394. {Original pointer in eax}
  6395. mov eax, esi
  6396. {Move the data across}
  6397. {$ifdef UseCustomVariableSizeMoveRoutines}
  6398. {$ifdef Align16Bytes}
  6399. call MoveX16LP
  6400. {$else}
  6401. call MoveX8LP
  6402. {$endif}
  6403. {$else}
  6404. call System.Move
  6405. {$endif}
  6406. {Free the original pointer}
  6407. mov eax, esi
  6408. call FastFreeMem
  6409. {Return the pointer}
  6410. mov eax, ebx
  6411. @SmallDownsizeDone:
  6412. pop esi
  6413. pop ebx
  6414. ret
  6415. {Align branch target}
  6416. nop
  6417. nop
  6418. @SmallUpsize:
  6419. {State: esi = APointer, edx = ANewSize, ecx = Current Block Size, ebx = Current Block Type}
  6420. {This pointer is being reallocated to a larger block and therefore it is
  6421. logical to assume that it may be enlarged again. Since reallocations are
  6422. expensive, there is a minimum upsize percentage to avoid unnecessary
  6423. future move operations.}
  6424. {Small blocks always grow with at least 100% + SmallBlockUpsizeAdder bytes}
  6425. lea ecx, [ecx + ecx + SmallBlockUpsizeAdder]
  6426. {save edi}
  6427. push edi
  6428. {Save the requested size in edi}
  6429. mov edi, edx
  6430. {New allocated size is the maximum of the requested size and the minimum
  6431. upsize}
  6432. xor eax, eax
  6433. sub ecx, edx
  6434. adc eax, -1
  6435. and eax, ecx
  6436. add eax, edx
  6437. {Allocate the new block}
  6438. call FastGetMem
  6439. {Allocated OK?}
  6440. test eax, eax
  6441. jz @SmallUpsizeDone
  6442. {Do we need to store the requested size? Only large blocks store the
  6443. requested size.}
  6444. cmp edi, MaximumMediumBlockSize - BlockHeaderSize
  6445. jbe @NotSmallUpsizeToLargeBlock
  6446. {Store the user requested size}
  6447. mov [eax - 8], edi
  6448. @NotSmallUpsizeToLargeBlock:
  6449. {Get the size to move across}
  6450. movzx ecx, TSmallBlockType[ebx].BlockSize
  6451. sub ecx, BlockHeaderSize
  6452. {Move to the new block}
  6453. mov edx, eax
  6454. {Save the result in edi}
  6455. mov edi, eax
  6456. {Move from the old block}
  6457. mov eax, esi
  6458. {Move the data across}
  6459. {$ifdef UseCustomFixedSizeMoveRoutines}
  6460. call TSmallBlockType[ebx].UpsizeMoveProcedure
  6461. {$else}
  6462. call System.Move
  6463. {$endif}
  6464. {Free the old pointer}
  6465. mov eax, esi
  6466. call FastFreeMem
  6467. {Done}
  6468. mov eax, edi
  6469. @SmallUpsizeDone:
  6470. pop edi
  6471. pop esi
  6472. pop ebx
  6473. ret
  6474. {Align branch target}
  6475. nop
  6476. @NotASmallBlock:
  6477. {Is this a medium block or a large block?}
  6478. test cl, IsFreeBlockFlag + IsLargeBlockFlag
  6479. jnz @PossibleLargeBlock
  6480. {-------------------------------Medium block--------------------------------------}
  6481. {Status: ecx = Current Block Size + Flags, eax/esi = APointer,
  6482. edx = Requested Size}
  6483. mov ebx, ecx
  6484. {Drop the flags from the header}
  6485. and ecx, DropMediumAndLargeFlagsMask
  6486. {Save edi}
  6487. push edi
  6488. {Get a pointer to the next block in edi}
  6489. lea edi, [eax + ecx]
  6490. {Subtract the block header size from the old available size}
  6491. sub ecx, BlockHeaderSize
  6492. {Get the complete flags in ebx}
  6493. and ebx, ExtractMediumAndLargeFlagsMask
  6494. {Is it an upsize or a downsize?}
  6495. cmp edx, ecx
  6496. {Save ebp}
  6497. push ebp
  6498. {Is it an upsize or a downsize?}
  6499. ja @MediumBlockUpsize
  6500. {Status: ecx = Current Block Size - 4, bl = Current Block Flags,
  6501. edi = @Next Block, eax/esi = APointer, edx = Requested Size}
  6502. {Must be less than half the current size or we don't bother resizing.}
  6503. lea ebp, [edx + edx]
  6504. cmp ebp, ecx
  6505. jb @MediumMustDownsize
  6506. @MediumNoResize:
  6507. {Restore registers}
  6508. pop ebp
  6509. pop edi
  6510. pop esi
  6511. pop ebx
  6512. {Return}
  6513. ret
  6514. {Align branch target}
  6515. nop
  6516. nop
  6517. nop
  6518. @MediumMustDownsize:
  6519. {In-place downsize? Balance the cost of moving the data vs. the cost of
  6520. fragmenting the memory pool. Medium blocks in use may never be smaller
  6521. than MinimumMediumBlockSize.}
  6522. cmp edx, MinimumMediumBlockSize - BlockHeaderSize
  6523. jae @MediumBlockInPlaceDownsize
  6524. {The requested size is less than the minimum medium block size. If the
  6525. requested size is less than the threshold value (currently a quarter of the
  6526. minimum medium block size), move the data to a small block, otherwise shrink
  6527. the medium block to the minimum allowable medium block size.}
  6528. cmp edx, MediumInPlaceDownsizeLimit
  6529. jb @MediumDownsizeRealloc
  6530. {The request is for a size smaller than the minimum medium block size, but
  6531. not small enough to justify moving data: Reduce the block size to the
  6532. minimum medium block size}
  6533. mov edx, MinimumMediumBlockSize - BlockHeaderSize
  6534. {Is it already at the minimum medium block size?}
  6535. cmp ecx, edx
  6536. jna @MediumNoResize
  6537. @MediumBlockInPlaceDownsize:
  6538. {Round up to the next medium block size}
  6539. lea ebp, [edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
  6540. and ebp, -MediumBlockGranularity;
  6541. add ebp, MediumBlockSizeOffset
  6542. {Get the size of the second split}
  6543. add ecx, BlockHeaderSize
  6544. sub ecx, ebp
  6545. {Lock the medium blocks}
  6546. {$ifndef AssumeMultiThreaded}
  6547. cmp IsMultiThread, False
  6548. je @DoMediumInPlaceDownsize
  6549. {$endif}
  6550. @DoMediumLockForDownsize:
  6551. {Lock the medium blocks (ecx *must* be preserved)}
  6552. call LockMediumBlocks
  6553. {Reread the flags - they may have changed before medium blocks could be
  6554. locked.}
  6555. mov ebx, ExtractMediumAndLargeFlagsMask
  6556. and ebx, [esi - 4]
  6557. @DoMediumInPlaceDownsize:
  6558. {Set the new size}
  6559. or ebx, ebp
  6560. mov [esi - 4], ebx
  6561. {Get the second split size in ebx}
  6562. mov ebx, ecx
  6563. {Is the next block in use?}
  6564. mov edx, [edi - 4]
  6565. test dl, IsFreeBlockFlag
  6566. jnz @MediumDownsizeNextBlockFree
  6567. {The next block is in use: flag its previous block as free}
  6568. or edx, PreviousMediumBlockIsFreeFlag
  6569. mov [edi - 4], edx
  6570. jmp @MediumDownsizeDoSplit
  6571. {Align branch target}
  6572. nop
  6573. nop
  6574. {$ifdef AssumeMultiThreaded}
  6575. nop
  6576. {$endif}
  6577. @MediumDownsizeNextBlockFree:
  6578. {The next block is free: combine it}
  6579. mov eax, edi
  6580. and edx, DropMediumAndLargeFlagsMask
  6581. add ebx, edx
  6582. add edi, edx
  6583. cmp edx, MinimumMediumBlockSize
  6584. jb @MediumDownsizeDoSplit
  6585. call RemoveMediumFreeBlock
  6586. @MediumDownsizeDoSplit:
  6587. {Store the trailing size field}
  6588. mov [edi - 8], ebx
  6589. {Store the free part's header}
  6590. lea eax, [ebx + IsMediumBlockFlag + IsFreeBlockFlag];
  6591. mov [esi + ebp - 4], eax
  6592. {Bin this free block}
  6593. cmp ebx, MinimumMediumBlockSize
  6594. jb @MediumBlockDownsizeDone
  6595. lea eax, [esi + ebp]
  6596. mov edx, ebx
  6597. call InsertMediumBlockIntoBin
  6598. @MediumBlockDownsizeDone:
  6599. {Unlock the medium blocks}
  6600. mov MediumBlocksLocked, False
  6601. {Result = old pointer}
  6602. mov eax, esi
  6603. {Restore registers}
  6604. pop ebp
  6605. pop edi
  6606. pop esi
  6607. pop ebx
  6608. {Return}
  6609. ret
  6610. {Align branch target}
  6611. @MediumDownsizeRealloc:
  6612. {Save the requested size}
  6613. mov edi, edx
  6614. mov eax, edx
  6615. {Allocate the new block}
  6616. call FastGetMem
  6617. test eax, eax
  6618. jz @MediumBlockDownsizeExit
  6619. {Save the result}
  6620. mov ebp, eax
  6621. mov edx, eax
  6622. mov eax, esi
  6623. mov ecx, edi
  6624. {Move the data across}
  6625. {$ifdef UseCustomVariableSizeMoveRoutines}
  6626. {$ifdef Align16Bytes}
  6627. call MoveX16LP
  6628. {$else}
  6629. call MoveX8LP
  6630. {$endif}
  6631. {$else}
  6632. call System.Move
  6633. {$endif}
  6634. mov eax, esi
  6635. call FastFreeMem
  6636. {Return the result}
  6637. mov eax, ebp
  6638. @MediumBlockDownsizeExit:
  6639. pop ebp
  6640. pop edi
  6641. pop esi
  6642. pop ebx
  6643. ret
  6644. {Align branch target}
  6645. @MediumBlockUpsize:
  6646. {Status: ecx = Current Block Size - 4, bl = Current Block Flags,
  6647. edi = @Next Block, eax/esi = APointer, edx = Requested Size}
  6648. {Can we do an in-place upsize?}
  6649. mov eax, [edi - 4]
  6650. test al, IsFreeBlockFlag
  6651. jz @CannotUpsizeMediumBlockInPlace
  6652. {Get the total available size including the next block}
  6653. and eax, DropMediumAndLargeFlagsMask
  6654. {ebp = total available size including the next block (excluding the header)}
  6655. lea ebp, [eax + ecx]
  6656. {Can the block fit?}
  6657. cmp edx, ebp
  6658. ja @CannotUpsizeMediumBlockInPlace
  6659. {The next block is free and there is enough space to grow this
  6660. block in place.}
  6661. {$ifndef AssumeMultiThreaded}
  6662. cmp IsMultiThread, False
  6663. je @DoMediumInPlaceUpsize
  6664. {$endif}
  6665. @DoMediumLockForUpsize:
  6666. {Lock the medium blocks (ecx and edx *must* be preserved}
  6667. call LockMediumBlocks
  6668. {Re-read the info for this block (since it may have changed before the medium
  6669. blocks could be locked)}
  6670. mov ebx, ExtractMediumAndLargeFlagsMask
  6671. and ebx, [esi - 4]
  6672. {Re-read the info for the next block}
  6673. mov eax, [edi - 4]
  6674. {Next block still free?}
  6675. test al, IsFreeBlockFlag
  6676. jz @NextMediumBlockChanged
  6677. {Recalculate the next block size}
  6678. and eax, DropMediumAndLargeFlagsMask
  6679. {The available size including the next block}
  6680. lea ebp, [eax + ecx]
  6681. {Can the block still fit?}
  6682. cmp edx, ebp
  6683. ja @NextMediumBlockChanged
  6684. @DoMediumInPlaceUpsize:
  6685. {Is the next block binnable?}
  6686. cmp eax, MinimumMediumBlockSize
  6687. {Remove the next block}
  6688. jb @MediumInPlaceNoNextRemove
  6689. mov eax, edi
  6690. push ecx
  6691. push edx
  6692. call RemoveMediumFreeBlock
  6693. pop edx
  6694. pop ecx
  6695. @MediumInPlaceNoNextRemove:
  6696. {Medium blocks grow a minimum of 25% in in-place upsizes}
  6697. mov eax, ecx
  6698. shr eax, 2
  6699. add eax, ecx
  6700. {Get the maximum of the requested size and the minimum growth size}
  6701. xor edi, edi
  6702. sub eax, edx
  6703. adc edi, -1
  6704. and eax, edi
  6705. {Round up to the nearest block size granularity}
  6706. lea eax, [eax + edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
  6707. and eax, -MediumBlockGranularity
  6708. add eax, MediumBlockSizeOffset
  6709. {Calculate the size of the second split}
  6710. lea edx, [ebp + BlockHeaderSize]
  6711. sub edx, eax
  6712. {Does it fit?}
  6713. ja @MediumInPlaceUpsizeSplit
  6714. {Grab the whole block: Mark it as used in the block following it}
  6715. and dword ptr [esi + ebp], not PreviousMediumBlockIsFreeFlag
  6716. {The block size is the full available size plus header}
  6717. add ebp, 4
  6718. {Upsize done}
  6719. jmp @MediumUpsizeInPlaceDone
  6720. {Align branch target}
  6721. {$ifndef AssumeMultiThreaded}
  6722. nop
  6723. nop
  6724. nop
  6725. {$endif}
  6726. @MediumInPlaceUpsizeSplit:
  6727. {Store the size of the second split as the second last dword}
  6728. mov [esi + ebp - 4], edx
  6729. {Set the second split header}
  6730. lea edi, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  6731. mov [esi + eax - 4], edi
  6732. mov ebp, eax
  6733. cmp edx, MinimumMediumBlockSize
  6734. jb @MediumUpsizeInPlaceDone
  6735. add eax, esi
  6736. call InsertMediumBlockIntoBin
  6737. @MediumUpsizeInPlaceDone:
  6738. {Set the size and flags for this block}
  6739. or ebp, ebx
  6740. mov [esi - 4], ebp
  6741. {Unlock the medium blocks}
  6742. mov MediumBlocksLocked, False
  6743. {Result = old pointer}
  6744. mov eax, esi
  6745. @MediumBlockResizeDone2:
  6746. {Restore registers}
  6747. pop ebp
  6748. pop edi
  6749. pop esi
  6750. pop ebx
  6751. {Return}
  6752. ret
  6753. {Align branch target for "@CannotUpsizeMediumBlockInPlace"}
  6754. nop
  6755. nop
  6756. @NextMediumBlockChanged:
  6757. {The next medium block changed while the medium blocks were being locked}
  6758. mov MediumBlocksLocked, False
  6759. @CannotUpsizeMediumBlockInPlace:
  6760. {Couldn't upsize in place. Grab a new block and move the data across:
  6761. If we have to reallocate and move medium blocks, we grow by at
  6762. least 25%}
  6763. mov eax, ecx
  6764. shr eax, 2
  6765. add eax, ecx
  6766. {Get the maximum of the requested size and the minimum growth size}
  6767. xor edi, edi
  6768. sub eax, edx
  6769. adc edi, -1
  6770. and eax, edi
  6771. add eax, edx
  6772. {Save the size to allocate}
  6773. mov ebp, eax
  6774. {Save the size to move across}
  6775. mov edi, ecx
  6776. {Get the block}
  6777. push edx
  6778. call FastGetMem
  6779. pop edx
  6780. {Success?}
  6781. test eax, eax
  6782. jz @MediumBlockResizeDone2
  6783. {If it's a Large block - store the actual user requested size}
  6784. cmp ebp, MaximumMediumBlockSize - BlockHeaderSize
  6785. jbe @MediumUpsizeNotLarge
  6786. mov [eax - 8], edx
  6787. @MediumUpsizeNotLarge:
  6788. {Save the result}
  6789. mov ebp, eax
  6790. {Move the data across}
  6791. mov edx, eax
  6792. mov eax, esi
  6793. mov ecx, edi
  6794. {$ifdef UseCustomVariableSizeMoveRoutines}
  6795. call MoveX16LP
  6796. {$else}
  6797. call System.Move
  6798. {$endif}
  6799. {Free the old block}
  6800. mov eax, esi
  6801. call FastFreeMem
  6802. {Restore the result}
  6803. mov eax, ebp
  6804. {Restore registers}
  6805. pop ebp
  6806. pop edi
  6807. pop esi
  6808. pop ebx
  6809. {Return}
  6810. ret
  6811. {Align branch target}
  6812. nop
  6813. @PossibleLargeBlock:
  6814. {-----------------------Large block------------------------------}
  6815. {Restore registers}
  6816. pop esi
  6817. pop ebx
  6818. {Is this a valid large block?}
  6819. test cl, IsFreeBlockFlag + IsMediumBlockFlag
  6820. jz ReallocateLargeBlock
  6821. {-----------------------Invalid block------------------------------}
  6822. xor eax, eax
  6823. end;
  6824. {$else}
  6825. {-----------------64-bit BASM FastReallocMem-----------------}
  6826. asm
  6827. .params 3
  6828. .pushnv rbx
  6829. .pushnv rsi
  6830. .pushnv rdi
  6831. .pushnv r14
  6832. .pushnv r15
  6833. {On entry: rcx = APointer; rdx = ANewSize}
  6834. {Save the original pointer in rsi}
  6835. mov rsi, rcx
  6836. {Get the block header}
  6837. mov rcx, [rcx - BlockHeaderSize]
  6838. {Is it a small block?}
  6839. test cl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
  6840. jnz @NotASmallBlock
  6841. {-----------------------------------Small block-------------------------------------}
  6842. {Get the block type in rbx}
  6843. mov rbx, TSmallBlockPoolHeader[rcx].BlockType
  6844. {Get the available size inside blocks of this type.}
  6845. movzx ecx, TSmallBlockType[rbx].BlockSize
  6846. sub ecx, BlockHeaderSize
  6847. {Is it an upsize or a downsize?}
  6848. cmp rcx, rdx
  6849. jb @SmallUpsize
  6850. {It's a downsize. Do we need to allocate a smaller block? Only if the new
  6851. size is less than a quarter of the available size less
  6852. SmallBlockDownsizeCheckAdder bytes}
  6853. lea ebx, [edx * 4 + SmallBlockDownsizeCheckAdder]
  6854. cmp ebx, ecx
  6855. jb @NotSmallInPlaceDownsize
  6856. {In-place downsize - return the original pointer}
  6857. mov rax, rsi
  6858. jmp @Done
  6859. @NotSmallInPlaceDownsize:
  6860. {Save the requested size}
  6861. mov rbx, rdx
  6862. {Allocate a smaller block}
  6863. mov rcx, rdx
  6864. call FastGetMem
  6865. {Allocated OK?}
  6866. test rax, rax
  6867. jz @Done
  6868. {Move data across: count in r8}
  6869. mov r8, rbx
  6870. {Destination in edx}
  6871. mov rdx, rax
  6872. {Save the result in ebx}
  6873. mov rbx, rax
  6874. {Original pointer in ecx}
  6875. mov rcx, rsi
  6876. {Move the data across}
  6877. {$ifdef UseCustomVariableSizeMoveRoutines}
  6878. {$ifdef Align16Bytes}
  6879. call MoveX16LP
  6880. {$else}
  6881. call MoveX8LP
  6882. {$endif}
  6883. {$else}
  6884. call System.Move
  6885. {$endif}
  6886. {Free the original pointer}
  6887. mov rcx, rsi
  6888. call FastFreeMem
  6889. {Return the pointer}
  6890. mov rax, rbx
  6891. jmp @Done
  6892. @SmallUpsize:
  6893. {State: rsi = APointer, rdx = ANewSize, rcx = Current Block Size, rbx = Current Block Type}
  6894. {This pointer is being reallocated to a larger block and therefore it is
  6895. logical to assume that it may be enlarged again. Since reallocations are
  6896. expensive, there is a minimum upsize percentage to avoid unnecessary
  6897. future move operations.}
  6898. {Small blocks always grow with at least 100% + SmallBlockUpsizeAdder bytes}
  6899. lea ecx, [ecx + ecx + SmallBlockUpsizeAdder]
  6900. {Save the requested size in rdi}
  6901. mov rdi, rdx
  6902. {New allocated size is the maximum of the requested size and the minimum
  6903. upsize}
  6904. xor rax, rax
  6905. sub rcx, rdx
  6906. adc rax, -1
  6907. and rcx, rax
  6908. add rcx, rdx
  6909. {Allocate the new block}
  6910. call FastGetMem
  6911. {Allocated OK?}
  6912. test rax, rax
  6913. jz @Done
  6914. {Do we need to store the requested size? Only large blocks store the
  6915. requested size.}
  6916. cmp rdi, MaximumMediumBlockSize - BlockHeaderSize
  6917. jbe @NotSmallUpsizeToLargeBlock
  6918. {Store the user requested size}
  6919. mov [rax - 2 * BlockHeaderSize], rdi
  6920. @NotSmallUpsizeToLargeBlock:
  6921. {Get the size to move across}
  6922. movzx r8d, TSmallBlockType[rbx].BlockSize
  6923. sub r8d, BlockHeaderSize
  6924. {Move to the new block}
  6925. mov rdx, rax
  6926. {Save the result in edi}
  6927. mov rdi, rax
  6928. {Move from the old block}
  6929. mov rcx, rsi
  6930. {Move the data across}
  6931. {$ifdef UseCustomFixedSizeMoveRoutines}
  6932. call TSmallBlockType[rbx].UpsizeMoveProcedure
  6933. {$else}
  6934. call System.Move
  6935. {$endif}
  6936. {Free the old pointer}
  6937. mov rcx, rsi
  6938. call FastFreeMem
  6939. {Done}
  6940. mov rax, rdi
  6941. jmp @Done
  6942. @NotASmallBlock:
  6943. {Is this a medium block or a large block?}
  6944. test cl, IsFreeBlockFlag + IsLargeBlockFlag
  6945. jnz @PossibleLargeBlock
  6946. {-------------------------------Medium block--------------------------------------}
  6947. {Status: rcx = Current Block Size + Flags, rsi = APointer,
  6948. rdx = Requested Size}
  6949. mov rbx, rcx
  6950. {Drop the flags from the header}
  6951. and ecx, DropMediumAndLargeFlagsMask
  6952. {Get a pointer to the next block in rdi}
  6953. lea rdi, [rsi + rcx]
  6954. {Subtract the block header size from the old available size}
  6955. sub ecx, BlockHeaderSize
  6956. {Get the complete flags in ebx}
  6957. and ebx, ExtractMediumAndLargeFlagsMask
  6958. {Is it an upsize or a downsize?}
  6959. cmp rdx, rcx
  6960. ja @MediumBlockUpsize
  6961. {Status: ecx = Current Block Size - BlockHeaderSize, bl = Current Block Flags,
  6962. rdi = @Next Block, rsi = APointer, rdx = Requested Size}
  6963. {Must be less than half the current size or we don't bother resizing.}
  6964. lea r15, [rdx + rdx]
  6965. cmp r15, rcx
  6966. jb @MediumMustDownsize
  6967. @MediumNoResize:
  6968. mov rax, rsi
  6969. jmp @Done
  6970. @MediumMustDownsize:
  6971. {In-place downsize? Balance the cost of moving the data vs. the cost of
  6972. fragmenting the memory pool. Medium blocks in use may never be smaller
  6973. than MinimumMediumBlockSize.}
  6974. cmp edx, MinimumMediumBlockSize - BlockHeaderSize
  6975. jae @MediumBlockInPlaceDownsize
  6976. {The requested size is less than the minimum medium block size. If the
  6977. requested size is less than the threshold value (currently a quarter of the
  6978. minimum medium block size), move the data to a small block, otherwise shrink
  6979. the medium block to the minimum allowable medium block size.}
  6980. cmp edx, MediumInPlaceDownsizeLimit
  6981. jb @MediumDownsizeRealloc
  6982. {The request is for a size smaller than the minimum medium block size, but
  6983. not small enough to justify moving data: Reduce the block size to the
  6984. minimum medium block size}
  6985. mov edx, MinimumMediumBlockSize - BlockHeaderSize
  6986. {Is it already at the minimum medium block size?}
  6987. cmp ecx, edx
  6988. jna @MediumNoResize
  6989. @MediumBlockInPlaceDownsize:
  6990. {Round up to the next medium block size}
  6991. lea r15, [rdx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
  6992. and r15, -MediumBlockGranularity
  6993. add r15, MediumBlockSizeOffset
  6994. {Get the size of the second split}
  6995. add ecx, BlockHeaderSize
  6996. sub ecx, r15d
  6997. {Lock the medium blocks}
  6998. {$ifndef AssumeMultiThreaded}
  6999. lea r8, IsMultiThread
  7000. cmp byte ptr [r8], False
  7001. je @DoMediumInPlaceDownsize
  7002. {$endif}
  7003. @DoMediumLockForDownsize:
  7004. {Lock the medium blocks}
  7005. mov ebx, ecx
  7006. call LockMediumBlocks
  7007. mov ecx, ebx
  7008. {Reread the flags - they may have changed before medium blocks could be
  7009. locked.}
  7010. mov rbx, ExtractMediumAndLargeFlagsMask
  7011. and rbx, [rsi - BlockHeaderSize]
  7012. @DoMediumInPlaceDownsize:
  7013. {Set the new size}
  7014. or rbx, r15
  7015. mov [rsi - BlockHeaderSize], rbx
  7016. {Get the second split size in ebx}
  7017. mov ebx, ecx
  7018. {Is the next block in use?}
  7019. mov rdx, [rdi - BlockHeaderSize]
  7020. test dl, IsFreeBlockFlag
  7021. jnz @MediumDownsizeNextBlockFree
  7022. {The next block is in use: flag its previous block as free}
  7023. or rdx, PreviousMediumBlockIsFreeFlag
  7024. mov [rdi - BlockHeaderSize], rdx
  7025. jmp @MediumDownsizeDoSplit
  7026. @MediumDownsizeNextBlockFree:
  7027. {The next block is free: combine it}
  7028. mov rcx, rdi
  7029. and rdx, DropMediumAndLargeFlagsMask
  7030. add rbx, rdx
  7031. add rdi, rdx
  7032. cmp edx, MinimumMediumBlockSize
  7033. jb @MediumDownsizeDoSplit
  7034. call RemoveMediumFreeBlock
  7035. @MediumDownsizeDoSplit:
  7036. {Store the trailing size field}
  7037. mov [rdi - 2 * BlockHeaderSize], rbx
  7038. {Store the free part's header}
  7039. lea rcx, [rbx + IsMediumBlockFlag + IsFreeBlockFlag];
  7040. mov [rsi + r15 - BlockHeaderSize], rcx
  7041. {Bin this free block}
  7042. cmp rbx, MinimumMediumBlockSize
  7043. jb @MediumBlockDownsizeDone
  7044. lea rcx, [rsi + r15]
  7045. mov rdx, rbx
  7046. call InsertMediumBlockIntoBin
  7047. @MediumBlockDownsizeDone:
  7048. {Unlock the medium blocks}
  7049. lea rax, MediumBlocksLocked
  7050. mov byte ptr [rax], False
  7051. {Result = old pointer}
  7052. mov rax, rsi
  7053. jmp @Done
  7054. @MediumDownsizeRealloc:
  7055. {Save the requested size}
  7056. mov rdi, rdx
  7057. mov rcx, rdx
  7058. {Allocate the new block}
  7059. call FastGetMem
  7060. test rax, rax
  7061. jz @Done
  7062. {Save the result}
  7063. mov r15, rax
  7064. mov rdx, rax
  7065. mov rcx, rsi
  7066. mov r8, rdi
  7067. {Move the data across}
  7068. {$ifdef UseCustomVariableSizeMoveRoutines}
  7069. {$ifdef Align16Bytes}
  7070. call MoveX16LP
  7071. {$else}
  7072. call MoveX8LP
  7073. {$endif}
  7074. {$else}
  7075. call System.Move
  7076. {$endif}
  7077. mov rcx, rsi
  7078. call FastFreeMem
  7079. {Return the result}
  7080. mov rax, r15
  7081. jmp @Done
  7082. @MediumBlockUpsize:
  7083. {Status: ecx = Current Block Size - BlockHeaderSize, bl = Current Block Flags,
  7084. rdi = @Next Block, rsi = APointer, rdx = Requested Size}
  7085. {Can we do an in-place upsize?}
  7086. mov rax, [rdi - BlockHeaderSize]
  7087. test al, IsFreeBlockFlag
  7088. jz @CannotUpsizeMediumBlockInPlace
  7089. {Get the total available size including the next block}
  7090. and rax, DropMediumAndLargeFlagsMask
  7091. {r15 = total available size including the next block (excluding the header)}
  7092. lea r15, [rax + rcx]
  7093. {Can the block fit?}
  7094. cmp rdx, r15
  7095. ja @CannotUpsizeMediumBlockInPlace
  7096. {The next block is free and there is enough space to grow this
  7097. block in place.}
  7098. {$ifndef AssumeMultiThreaded}
  7099. lea r8, IsMultiThread
  7100. cmp byte ptr [r8], False
  7101. je @DoMediumInPlaceUpsize
  7102. {$endif}
  7103. @DoMediumLockForUpsize:
  7104. {Lock the medium blocks.}
  7105. mov rbx, rcx
  7106. mov r15, rdx
  7107. call LockMediumBlocks
  7108. mov rcx, rbx
  7109. mov rdx, r15
  7110. {Re-read the info for this block (since it may have changed before the medium
  7111. blocks could be locked)}
  7112. mov rbx, ExtractMediumAndLargeFlagsMask
  7113. and rbx, [rsi - BlockHeaderSize]
  7114. {Re-read the info for the next block}
  7115. mov rax, [rdi - BlockheaderSize]
  7116. {Next block still free?}
  7117. test al, IsFreeBlockFlag
  7118. jz @NextMediumBlockChanged
  7119. {Recalculate the next block size}
  7120. and eax, DropMediumAndLargeFlagsMask
  7121. {The available size including the next block}
  7122. lea r15, [rax + rcx]
  7123. {Can the block still fit?}
  7124. cmp rdx, r15
  7125. ja @NextMediumBlockChanged
  7126. @DoMediumInPlaceUpsize:
  7127. {Is the next block binnable?}
  7128. cmp eax, MinimumMediumBlockSize
  7129. {Remove the next block}
  7130. jb @MediumInPlaceNoNextRemove
  7131. mov r14, rcx
  7132. mov rcx, rdi
  7133. mov rdi, rdx
  7134. call RemoveMediumFreeBlock
  7135. mov rcx, r14
  7136. mov rdx, rdi
  7137. @MediumInPlaceNoNextRemove:
  7138. {Medium blocks grow a minimum of 25% in in-place upsizes}
  7139. mov eax, ecx
  7140. shr eax, 2
  7141. add eax, ecx
  7142. {Get the maximum of the requested size and the minimum growth size}
  7143. xor edi, edi
  7144. sub eax, edx
  7145. adc edi, -1
  7146. and eax, edi
  7147. {Round up to the nearest block size granularity}
  7148. lea eax, [eax + edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
  7149. and eax, -MediumBlockGranularity
  7150. add eax, MediumBlockSizeOffset
  7151. {Calculate the size of the second split}
  7152. lea rdx, [r15 + BlockHeaderSize]
  7153. sub edx, eax
  7154. {Does it fit?}
  7155. ja @MediumInPlaceUpsizeSplit
  7156. {Grab the whole block: Mark it as used in the block following it}
  7157. and qword ptr [rsi + r15], not PreviousMediumBlockIsFreeFlag
  7158. {The block size is the full available size plus header}
  7159. add r15, BlockHeaderSize
  7160. {Upsize done}
  7161. jmp @MediumUpsizeInPlaceDone
  7162. @MediumInPlaceUpsizeSplit:
  7163. {Store the size of the second split as the second last dword}
  7164. mov [rsi + r15 - BlockHeaderSize], rdx
  7165. {Set the second split header}
  7166. lea edi, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  7167. mov [rsi + rax - BlockHeaderSize], rdi
  7168. mov r15, rax
  7169. cmp edx, MinimumMediumBlockSize
  7170. jb @MediumUpsizeInPlaceDone
  7171. lea rcx, [rsi + rax]
  7172. call InsertMediumBlockIntoBin
  7173. @MediumUpsizeInPlaceDone:
  7174. {Set the size and flags for this block}
  7175. or r15, rbx
  7176. mov [rsi - BlockHeaderSize], r15
  7177. {Unlock the medium blocks}
  7178. lea rax, MediumBlocksLocked
  7179. mov byte ptr [rax], False
  7180. {Result = old pointer}
  7181. mov rax, rsi
  7182. jmp @Done
  7183. @NextMediumBlockChanged:
  7184. {The next medium block changed while the medium blocks were being locked}
  7185. lea rax, MediumBlocksLocked
  7186. mov byte ptr [rax], False
  7187. @CannotUpsizeMediumBlockInPlace:
  7188. {Couldn't upsize in place. Grab a new block and move the data across:
  7189. If we have to reallocate and move medium blocks, we grow by at
  7190. least 25%}
  7191. mov eax, ecx
  7192. shr eax, 2
  7193. add eax, ecx
  7194. {Get the maximum of the requested size and the minimum growth size}
  7195. xor rdi, rdi
  7196. sub rax, rdx
  7197. adc rdi, -1
  7198. and rax, rdi
  7199. add rax, rdx
  7200. {Save the size to allocate}
  7201. mov r15, rax
  7202. {Save the size to move across}
  7203. mov edi, ecx
  7204. {Save the requested size}
  7205. mov rbx, rdx
  7206. {Get the block}
  7207. mov rcx, rax
  7208. call FastGetMem
  7209. mov rdx, rbx
  7210. {Success?}
  7211. test eax, eax
  7212. jz @Done
  7213. {If it's a Large block - store the actual user requested size}
  7214. cmp r15, MaximumMediumBlockSize - BlockHeaderSize
  7215. jbe @MediumUpsizeNotLarge
  7216. mov [rax - 2 * BlockHeaderSize], rdx
  7217. @MediumUpsizeNotLarge:
  7218. {Save the result}
  7219. mov r15, rax
  7220. {Move the data across}
  7221. mov rdx, rax
  7222. mov rcx, rsi
  7223. mov r8, rdi
  7224. {$ifdef UseCustomVariableSizeMoveRoutines}
  7225. call MoveX16LP
  7226. {$else}
  7227. call System.Move
  7228. {$endif}
  7229. {Free the old block}
  7230. mov rcx, rsi
  7231. call FastFreeMem
  7232. {Restore the result}
  7233. mov rax, r15
  7234. jmp @Done
  7235. @PossibleLargeBlock:
  7236. {-----------------------Large block------------------------------}
  7237. {Is this a valid large block?}
  7238. test cl, IsFreeBlockFlag + IsMediumBlockFlag
  7239. jnz @Error
  7240. mov rcx, rsi
  7241. call ReallocateLargeBlock
  7242. jmp @Done
  7243. {-----------------------Invalid block------------------------------}
  7244. @Error:
  7245. xor eax, eax
  7246. @Done:
  7247. end;
  7248. {$endif}
  7249. {$endif}
  7250. {$endif}
  7251. {Allocates a block and fills it with zeroes}
  7252. function FastAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  7253. {$ifndef ASMVersion}
  7254. begin
  7255. Result := FastGetMem(ASize);
  7256. {Large blocks are already zero filled}
  7257. if (Result <> nil) and (ASize <= (MaximumMediumBlockSize - BlockHeaderSize)) then
  7258. FillChar(Result^, ASize, 0);
  7259. end;
  7260. {$else}
  7261. {$ifdef 32Bit}
  7262. asm
  7263. push ebx
  7264. {Get the size rounded down to the previous multiple of 4 into ebx}
  7265. lea ebx, [eax - 1]
  7266. and ebx, -4
  7267. {Get the block}
  7268. call FastGetMem
  7269. {Could a block be allocated? ecx = 0 if yes, $ffffffff if no}
  7270. cmp eax, 1
  7271. sbb ecx, ecx
  7272. {Point edx to the last dword}
  7273. lea edx, [eax + ebx]
  7274. {ebx = $ffffffff if no block could be allocated, otherwise size rounded down
  7275. to previous multiple of 4. If ebx = 0 then the block size is 1..4 bytes and
  7276. the FPU based clearing loop should not be used (since it clears 8 bytes per
  7277. iteration).}
  7278. or ebx, ecx
  7279. jz @ClearLastDWord
  7280. {Large blocks are already zero filled}
  7281. cmp ebx, MaximumMediumBlockSize - BlockHeaderSize
  7282. jae @Done
  7283. {Make the counter negative based}
  7284. neg ebx
  7285. {Load zero into st(0)}
  7286. fldz
  7287. {Clear groups of 8 bytes. Block sizes are always four less than a multiple
  7288. of 8.}
  7289. @FillLoop:
  7290. fst qword ptr [edx + ebx]
  7291. add ebx, 8
  7292. js @FillLoop
  7293. {Clear st(0)}
  7294. ffree st(0)
  7295. {Correct the stack top}
  7296. fincstp
  7297. {Clear the last four bytes}
  7298. @ClearLastDWord:
  7299. mov [edx], ecx
  7300. @Done:
  7301. pop ebx
  7302. end;
  7303. {$else}
  7304. {---------------64-bit BASM FastAllocMem---------------}
  7305. asm
  7306. .params 1
  7307. .pushnv rbx
  7308. {Get the size rounded down to the previous multiple of SizeOf(Pointer) into
  7309. ebx}
  7310. lea rbx, [rcx - 1]
  7311. and rbx, -8
  7312. {Get the block}
  7313. call FastGetMem
  7314. {Could a block be allocated? rcx = 0 if yes, -1 if no}
  7315. cmp rax, 1
  7316. sbb rcx, rcx
  7317. {Point rdx to the last dword}
  7318. lea rdx, [rax + rbx]
  7319. {rbx = -1 if no block could be allocated, otherwise size rounded down
  7320. to previous multiple of 8. If rbx = 0 then the block size is 1..8 bytes and
  7321. the SSE2 based clearing loop should not be used (since it clears 16 bytes per
  7322. iteration).}
  7323. or rbx, rcx
  7324. jz @ClearLastQWord
  7325. {Large blocks are already zero filled}
  7326. cmp rbx, MaximumMediumBlockSize - BlockHeaderSize
  7327. jae @Done
  7328. {Make the counter negative based}
  7329. neg rbx
  7330. {Load zero into st(0)}
  7331. pxor xmm0, xmm0
  7332. {Clear groups of 16 bytes. Block sizes are always 8 less than a multiple of
  7333. 16.}
  7334. @FillLoop:
  7335. movdqa [rdx + rbx], xmm0
  7336. add rbx, 16
  7337. js @FillLoop
  7338. {Clear the last 8 bytes}
  7339. @ClearLastQWord:
  7340. xor rcx, rcx
  7341. mov [rdx], rcx
  7342. @Done:
  7343. end;
  7344. {$endif}
  7345. {$endif}
  7346. {-----------------Post Uninstall GetMem/FreeMem/ReallocMem-------------------}
  7347. {$ifdef DetectMMOperationsAfterUninstall}
  7348. function InvalidGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  7349. {$ifndef NoMessageBoxes}
  7350. var
  7351. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7352. {$endif}
  7353. begin
  7354. {$ifdef UseOutputDebugString}
  7355. OutputDebugStringA(InvalidGetMemMsg);
  7356. {$endif}
  7357. {$ifndef NoMessageBoxes}
  7358. AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
  7359. ShowMessageBox(InvalidGetMemMsg, LErrorMessageTitle);
  7360. {$endif}
  7361. Result := nil;
  7362. end;
  7363. function InvalidFreeMem(APointer: Pointer): Integer;
  7364. {$ifndef NoMessageBoxes}
  7365. var
  7366. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7367. {$endif}
  7368. begin
  7369. {$ifdef UseOutputDebugString}
  7370. OutputDebugStringA(InvalidFreeMemMsg);
  7371. {$endif}
  7372. {$ifndef NoMessageBoxes}
  7373. AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
  7374. ShowMessageBox(InvalidFreeMemMsg, LErrorMessageTitle);
  7375. {$endif}
  7376. Result := -1;
  7377. end;
  7378. function InvalidReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  7379. {$ifndef NoMessageBoxes}
  7380. var
  7381. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7382. {$endif}
  7383. begin
  7384. {$ifdef UseOutputDebugString}
  7385. OutputDebugStringA(InvalidReallocMemMsg);
  7386. {$endif}
  7387. {$ifndef NoMessageBoxes}
  7388. AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
  7389. ShowMessageBox(InvalidReallocMemMsg, LErrorMessageTitle);
  7390. {$endif}
  7391. Result := nil;
  7392. end;
  7393. function InvalidAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  7394. {$ifndef NoMessageBoxes}
  7395. var
  7396. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7397. {$endif}
  7398. begin
  7399. {$ifdef UseOutputDebugString}
  7400. OutputDebugStringA(InvalidAllocMemMsg);
  7401. {$endif}
  7402. {$ifndef NoMessageBoxes}
  7403. AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
  7404. ShowMessageBox(InvalidAllocMemMsg, LErrorMessageTitle);
  7405. {$endif}
  7406. Result := nil;
  7407. end;
  7408. function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean;
  7409. begin
  7410. Result := False;
  7411. end;
  7412. {$endif}
  7413. {-----------------Full Debug Mode Memory Manager Interface--------------------}
  7414. {$ifdef FullDebugMode}
  7415. {Compare [AAddress], CompareVal:
  7416. If Equal: [AAddress] := NewVal and result = CompareVal
  7417. If Unequal: Result := [AAddress]}
  7418. function LockCmpxchg32(CompareVal, NewVal: Integer; AAddress: PInteger): Integer;
  7419. asm
  7420. {$ifdef 32Bit}
  7421. {On entry:
  7422. eax = CompareVal,
  7423. edx = NewVal,
  7424. ecx = AAddress}
  7425. lock cmpxchg [ecx], edx
  7426. {$else}
  7427. .noframe
  7428. {On entry:
  7429. ecx = CompareVal,
  7430. edx = NewVal,
  7431. r8 = AAddress}
  7432. mov eax, ecx
  7433. lock cmpxchg [r8], edx
  7434. {$endif}
  7435. end;
  7436. {Called by DebugGetMem, DebugFreeMem and DebugReallocMem in order to block a
  7437. free block scan operation while the memory pool is being modified.}
  7438. procedure StartChangingFullDebugModeBlock;
  7439. var
  7440. LOldCount: Integer;
  7441. begin
  7442. while True do
  7443. begin
  7444. {Get the old thread count}
  7445. LOldCount := ThreadsInFullDebugModeRoutine;
  7446. if (LOldCount >= 0)
  7447. and (LockCmpxchg32(LOldCount, LOldCount + 1, @ThreadsInFullDebugModeRoutine) = LOldCount) then
  7448. begin
  7449. Break;
  7450. end;
  7451. {$ifdef NeverSleepOnThreadContention}
  7452. {$ifdef UseSwitchToThread}
  7453. SwitchToThread;
  7454. {$endif}
  7455. {$else}
  7456. Sleep(InitialSleepTime);
  7457. {Try again}
  7458. LOldCount := ThreadsInFullDebugModeRoutine;
  7459. if (LOldCount >= 0)
  7460. and (LockCmpxchg32(LOldCount, LOldCount + 1, @ThreadsInFullDebugModeRoutine) = LOldCount) then
  7461. begin
  7462. Break;
  7463. end;
  7464. Sleep(AdditionalSleepTime);
  7465. {$endif}
  7466. end;
  7467. end;
  7468. procedure DoneChangingFullDebugModeBlock;
  7469. asm
  7470. {$ifdef 32Bit}
  7471. lock dec ThreadsInFullDebugModeRoutine
  7472. {$else}
  7473. .noframe
  7474. lea rax, ThreadsInFullDebugModeRoutine
  7475. lock dec dword ptr [rax]
  7476. {$endif}
  7477. end;
  7478. {Increments the allocation number}
  7479. procedure IncrementAllocationNumber;
  7480. asm
  7481. {$ifdef 32Bit}
  7482. lock inc CurrentAllocationNumber
  7483. {$else}
  7484. .noframe
  7485. lea rax, CurrentAllocationNumber
  7486. lock inc dword ptr [rax]
  7487. {$endif}
  7488. end;
  7489. {Called by a routine wanting to lock the entire memory pool in FullDebugMode, e.g. before scanning the memory
  7490. pool for corruptions.}
  7491. procedure BlockFullDebugModeMMRoutines;
  7492. begin
  7493. while True do
  7494. begin
  7495. {Get the old thread count}
  7496. if LockCmpxchg32(0, -1, @ThreadsInFullDebugModeRoutine) = 0 then
  7497. Break;
  7498. {$ifdef NeverSleepOnThreadContention}
  7499. {$ifdef UseSwitchToThread}
  7500. SwitchToThread;
  7501. {$endif}
  7502. {$else}
  7503. Sleep(InitialSleepTime);
  7504. {Try again}
  7505. if LockCmpxchg32(0, -1, @ThreadsInFullDebugModeRoutine) = 0 then
  7506. Break;
  7507. Sleep(AdditionalSleepTime);
  7508. {$endif}
  7509. end;
  7510. end;
  7511. procedure UnblockFullDebugModeMMRoutines;
  7512. begin
  7513. {Currently blocked? If so, unblock the FullDebugMode routines.}
  7514. if ThreadsInFullDebugModeRoutine = -1 then
  7515. ThreadsInFullDebugModeRoutine := 0;
  7516. end;
  7517. procedure DeleteEventLog;
  7518. begin
  7519. {Delete the file}
  7520. DeleteFileA(MMLogFileName);
  7521. end;
  7522. {Finds the start and length of the file name given a full path.}
  7523. procedure ExtractFileName(APFullPath: PAnsiChar; var APFileNameStart: PAnsiChar; var AFileNameLength: Integer);
  7524. var
  7525. LChar: AnsiChar;
  7526. begin
  7527. {Initialize}
  7528. APFileNameStart := APFullPath;
  7529. AFileNameLength := 0;
  7530. {Find the file }
  7531. while True do
  7532. begin
  7533. {Get the next character}
  7534. LChar := APFullPath^;
  7535. {End of the path string?}
  7536. if LChar = #0 then
  7537. Break;
  7538. {Advance the buffer position}
  7539. Inc(APFullPath);
  7540. {Found a backslash? -> May be the start of the file name}
  7541. if LChar = '\' then
  7542. APFileNameStart := APFullPath;
  7543. end;
  7544. {Calculate the length of the file name}
  7545. AFileNameLength := IntPtr(APFullPath) - IntPtr(APFileNameStart);
  7546. end;
  7547. procedure AppendEventLog(ABuffer: Pointer; ACount: Cardinal);
  7548. const
  7549. {Declared here, because it is not declared in the SHFolder.pas unit of some older Delphi versions.}
  7550. SHGFP_TYPE_CURRENT = 0;
  7551. var
  7552. LFileHandle, LBytesWritten: Cardinal;
  7553. LEventHeader: array[0..1023] of AnsiChar;
  7554. LAlternateLogFileName: array[0..2047] of AnsiChar;
  7555. LPathLen, LNameLength: Integer;
  7556. LMsgPtr, LPFileName: PAnsiChar;
  7557. LSystemTime: TSystemTime;
  7558. begin
  7559. {Try to open the log file in read/write mode.}
  7560. LFileHandle := CreateFileA(MMLogFileName, GENERIC_READ or GENERIC_WRITE,
  7561. 0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  7562. {Did log file creation fail? If so, the destination folder is perhaps read-only:
  7563. Try to redirect logging to a file in the user's "My Documents" folder.}
  7564. if (LFileHandle = INVALID_HANDLE_VALUE)
  7565. {$ifdef Delphi4or5}
  7566. and SHGetSpecialFolderPathA(0, @LAlternateLogFileName, CSIDL_PERSONAL, True) then
  7567. {$else}
  7568. and (SHGetFolderPathA(0, CSIDL_PERSONAL or CSIDL_FLAG_CREATE, 0,
  7569. SHGFP_TYPE_CURRENT, @LAlternateLogFileName) = S_OK) then
  7570. {$endif}
  7571. begin
  7572. {Extract the filename part from MMLogFileName and append it to the path of
  7573. the "My Documents" folder.}
  7574. LPathLen := StrLen(LAlternateLogFileName);
  7575. {Ensure that there is a trailing backslash in the path}
  7576. if (LPathLen = 0) or (LAlternateLogFileName[LPathLen - 1] <> '\') then
  7577. begin
  7578. LAlternateLogFileName[LPathLen] := '\';
  7579. Inc(LPathLen);
  7580. end;
  7581. {Add the filename to the path}
  7582. ExtractFileName(@MMLogFileName, LPFileName, LNameLength);
  7583. System.Move(LPFileName^, LAlternateLogFileName[LPathLen], LNameLength + 1);
  7584. {Try to open the alternate log file}
  7585. LFileHandle := CreateFileA(LAlternateLogFileName, GENERIC_READ or GENERIC_WRITE,
  7586. 0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  7587. end;
  7588. {Was the log file opened/created successfully?}
  7589. if LFileHandle <> INVALID_HANDLE_VALUE then
  7590. begin
  7591. {Seek to the end of the file}
  7592. SetFilePointer(LFileHandle, 0, nil, FILE_END);
  7593. {Set the separator}
  7594. LMsgPtr := AppendStringToBuffer(CRLF, @LEventHeader[0], Length(CRLF));
  7595. LMsgPtr := AppendStringToBuffer(EventSeparator, LMsgPtr, Length(EventSeparator));
  7596. {Set the date & time}
  7597. GetLocalTime(LSystemTime);
  7598. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wYear, LMsgPtr);
  7599. LMsgPtr^ := '/';
  7600. Inc(LMsgPtr);
  7601. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wMonth, LMsgPtr);
  7602. LMsgPtr^ := '/';
  7603. Inc(LMsgPtr);
  7604. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wDay, LMsgPtr);
  7605. LMsgPtr^ := ' ';
  7606. Inc(LMsgPtr);
  7607. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wHour, LMsgPtr);
  7608. LMsgPtr^ := ':';
  7609. Inc(LMsgPtr);
  7610. if LSystemTime.wMinute < 10 then
  7611. begin
  7612. LMsgPtr^ := '0';
  7613. Inc(LMsgPtr);
  7614. end;
  7615. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wMinute, LMsgPtr);
  7616. LMsgPtr^ := ':';
  7617. Inc(LMsgPtr);
  7618. if LSystemTime.wSecond < 10 then
  7619. begin
  7620. LMsgPtr^ := '0';
  7621. Inc(LMsgPtr);
  7622. end;
  7623. LMsgPtr := NativeUIntToStrBuf(LSystemTime.WSecond, LMsgPtr);
  7624. {Write the header}
  7625. LMsgPtr := AppendStringToBuffer(EventSeparator, LMsgPtr, Length(EventSeparator));
  7626. LMsgPtr := AppendStringToBuffer(CRLF, LMsgPtr, Length(CRLF));
  7627. WriteFile(LFileHandle, LEventHeader[0], NativeUInt(LMsgPtr) - NativeUInt(@LEventHeader[0]), LBytesWritten, nil);
  7628. {Write the data}
  7629. WriteFile(LFileHandle, ABuffer^, ACount, LBytesWritten, nil);
  7630. {Close the file}
  7631. CloseHandle(LFileHandle);
  7632. end;
  7633. end;
  7634. {Sets the default log filename}
  7635. procedure SetDefaultMMLogFileName;
  7636. const
  7637. LogFileExtAnsi: PAnsiChar = LogFileExtension;
  7638. var
  7639. LEnvVarLength, LModuleNameLength: Cardinal;
  7640. LPathOverride: array[0..2047] of AnsiChar;
  7641. LPFileName: PAnsiChar;
  7642. LFileNameLength: Integer;
  7643. begin
  7644. {Get the name of the application}
  7645. LModuleNameLength := AppendModuleFileName(@MMLogFileName[0]);
  7646. {Replace the last few characters of the module name, and optionally override
  7647. the path.}
  7648. if LModuleNameLength > 0 then
  7649. begin
  7650. {Change the filename}
  7651. System.Move(LogFileExtAnsi^, MMLogFileName[LModuleNameLength - 4],
  7652. StrLen(LogFileExtAnsi) + 1);
  7653. {Try to read the FastMMLogFilePath environment variable}
  7654. LEnvVarLength := GetEnvironmentVariableA('FastMMLogFilePath',
  7655. @LPathOverride, 1023);
  7656. {Does the environment variable exist? If so, override the log file path.}
  7657. if LEnvVarLength > 0 then
  7658. begin
  7659. {Ensure that there's a trailing backslash.}
  7660. if LPathOverride[LEnvVarLength - 1] <> '\' then
  7661. begin
  7662. LPathOverride[LEnvVarLength] := '\';
  7663. Inc(LEnvVarLength);
  7664. end;
  7665. {Add the filename to the path override}
  7666. ExtractFileName(@MMLogFileName[0], LPFileName, LFileNameLength);
  7667. System.Move(LPFileName^, LPathOverride[LEnvVarLength], LFileNameLength + 1);
  7668. {Copy the override path back to the filename buffer}
  7669. System.Move(LPathOverride, MMLogFileName, SizeOf(MMLogFileName) - 1);
  7670. end;
  7671. end;
  7672. end;
  7673. {Specify the full path and name for the filename to be used for logging memory
  7674. errors, etc. If ALogFileName is nil or points to an empty string it will
  7675. revert to the default log file name.}
  7676. procedure SetMMLogFileName(ALogFileName: PAnsiChar = nil);
  7677. var
  7678. LLogFileNameLen: Integer;
  7679. begin
  7680. {Is ALogFileName valid?}
  7681. if (ALogFileName <> nil) and (ALogFileName^ <> #0) then
  7682. begin
  7683. LLogFileNameLen := StrLen(ALogFileName);
  7684. if LLogFileNameLen < Length(MMLogFileName) then
  7685. begin
  7686. {Set the log file name}
  7687. System.Move(ALogFileName^, MMLogFileName, LLogFileNameLen + 1);
  7688. Exit;
  7689. end;
  7690. end;
  7691. {Invalid log file name}
  7692. SetDefaultMMLogFileName;
  7693. end;
  7694. {Returns the current "allocation group". Whenever a GetMem request is serviced
  7695. in FullDebugMode, the current "allocation group" is stored in the block header.
  7696. This may help with debugging. Note that if a block is subsequently reallocated
  7697. that it keeps its original "allocation group" and "allocation number" (all
  7698. allocations are also numbered sequentially).}
  7699. function GetCurrentAllocationGroup: Cardinal;
  7700. begin
  7701. Result := AllocationGroupStack[AllocationGroupStackTop];
  7702. end;
  7703. {Allocation groups work in a stack like fashion. Group numbers are pushed onto
  7704. and popped off the stack. Note that the stack size is limited, so every push
  7705. should have a matching pop.}
  7706. procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
  7707. begin
  7708. if AllocationGroupStackTop < AllocationGroupStackSize - 1 then
  7709. begin
  7710. Inc(AllocationGroupStackTop);
  7711. AllocationGroupStack[AllocationGroupStackTop] := ANewCurrentAllocationGroup;
  7712. end
  7713. else
  7714. begin
  7715. {Raise a runtime error if the stack overflows}
  7716. {$ifdef BCB6OrDelphi7AndUp}
  7717. System.Error(reInvalidPtr);
  7718. {$else}
  7719. System.RunError(reInvalidPtr);
  7720. {$endif}
  7721. end;
  7722. end;
  7723. procedure PopAllocationGroup;
  7724. begin
  7725. if AllocationGroupStackTop > 0 then
  7726. begin
  7727. Dec(AllocationGroupStackTop);
  7728. end
  7729. else
  7730. begin
  7731. {Raise a runtime error if the stack underflows}
  7732. {$ifdef BCB6OrDelphi7AndUp}
  7733. System.Error(reInvalidPtr);
  7734. {$else}
  7735. System.RunError(reInvalidPtr);
  7736. {$endif}
  7737. end;
  7738. end;
  7739. {Sums all the dwords starting at the given address. ACount must be > 0 and a
  7740. multiple of SizeOf(Pointer).}
  7741. function SumNativeUInts(AStartValue: NativeUInt; APointer: PNativeUInt;
  7742. ACount: NativeUInt): NativeUInt;
  7743. asm
  7744. {$ifdef 32Bit}
  7745. {On entry: eax = AStartValue, edx = APointer; ecx = ACount}
  7746. add edx, ecx
  7747. neg ecx
  7748. @AddLoop:
  7749. add eax, [edx + ecx]
  7750. add ecx, 4
  7751. js @AddLoop
  7752. {$else}
  7753. {On entry: rcx = AStartValue, rdx = APointer; r8 = ACount}
  7754. add rdx, r8
  7755. neg r8
  7756. mov rax, rcx
  7757. @AddLoop:
  7758. add rax, [rdx + r8]
  7759. add r8, 8
  7760. js @AddLoop
  7761. {$endif}
  7762. end;
  7763. {Checks the memory starting at the given address for the fill pattern.
  7764. Returns True if all bytes are all valid. ACount must be >0 and a multiple of
  7765. SizeOf(Pointer).}
  7766. function CheckFillPattern(APointer: Pointer; ACount: NativeUInt;
  7767. AFillPattern: NativeUInt): Boolean;
  7768. asm
  7769. {$ifdef 32Bit}
  7770. {On entry: eax = APointer; edx = ACount; ecx = AFillPattern}
  7771. add eax, edx
  7772. neg edx
  7773. @CheckLoop:
  7774. cmp [eax + edx], ecx
  7775. jne @Done
  7776. add edx, 4
  7777. js @CheckLoop
  7778. @Done:
  7779. sete al
  7780. {$else}
  7781. {On entry: rcx = APointer; rdx = ACount; r8 = AFillPattern}
  7782. add rcx, rdx
  7783. neg rdx
  7784. @CheckLoop:
  7785. cmp [rcx + rdx], r8
  7786. jne @Done
  7787. add rdx, 8
  7788. js @CheckLoop
  7789. @Done:
  7790. sete al
  7791. {$endif}
  7792. end;
  7793. {Calculates the checksum for the debug header. Adds all dwords in the debug
  7794. header to the start address of the block.}
  7795. function CalculateHeaderCheckSum(APointer: PFullDebugBlockHeader): NativeUInt;
  7796. begin
  7797. Result := SumNativeUInts(
  7798. NativeUInt(APointer),
  7799. PNativeUInt(PByte(APointer) + 2 * SizeOf(Pointer)),
  7800. SizeOf(TFullDebugBlockHeader) - 2 * SizeOf(Pointer) - SizeOf(NativeUInt));
  7801. end;
  7802. procedure UpdateHeaderAndFooterCheckSums(APointer: PFullDebugBlockHeader);
  7803. var
  7804. LHeaderCheckSum: NativeUInt;
  7805. begin
  7806. LHeaderCheckSum := CalculateHeaderCheckSum(APointer);
  7807. APointer.HeaderCheckSum := LHeaderCheckSum;
  7808. PNativeUInt(PByte(APointer) + SizeOf(TFullDebugBlockHeader) + APointer.UserSize)^ := not LHeaderCheckSum;
  7809. end;
  7810. function LogCurrentThreadAndStackTrace(ASkipFrames: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
  7811. var
  7812. LCurrentStackTrace: TStackTrace;
  7813. begin
  7814. {Get the current call stack}
  7815. GetStackTrace(@LCurrentStackTrace[0], StackTraceDepth, ASkipFrames);
  7816. {Log the thread ID}
  7817. Result := AppendStringToBuffer(CurrentThreadIDMsg, ABuffer, Length(CurrentThreadIDMsg));
  7818. Result := NativeUIntToHexBuf(GetThreadID, Result);
  7819. {List the stack trace}
  7820. Result := AppendStringToBuffer(CurrentStackTraceMsg, Result, Length(CurrentStackTraceMsg));
  7821. Result := LogStackTrace(@LCurrentStackTrace, StackTraceDepth, Result);
  7822. end;
  7823. {$ifndef DisableLoggingOfMemoryDumps}
  7824. function LogMemoryDump(APointer: PFullDebugBlockHeader; ABuffer: PAnsiChar): PAnsiChar;
  7825. var
  7826. LByteNum, LVal: Cardinal;
  7827. LDataPtr: PByte;
  7828. begin
  7829. Result := AppendStringToBuffer(MemoryDumpMsg, ABuffer, Length(MemoryDumpMsg));
  7830. Result := NativeUIntToHexBuf(NativeUInt(APointer) + SizeOf(TFullDebugBlockHeader), Result);
  7831. Result^ := ':';
  7832. Inc(Result);
  7833. {Add the bytes}
  7834. LDataPtr := PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader));
  7835. for LByteNum := 0 to 255 do
  7836. begin
  7837. if LByteNum and 31 = 0 then
  7838. begin
  7839. Result^ := #13;
  7840. Inc(Result);
  7841. Result^ := #10;
  7842. Inc(Result);
  7843. end
  7844. else
  7845. begin
  7846. Result^ := ' ';
  7847. Inc(Result);
  7848. end;
  7849. {Set the hex data}
  7850. LVal := Byte(LDataPtr^);
  7851. Result^ := HexTable[LVal shr 4];
  7852. Inc(Result);
  7853. Result^ := HexTable[LVal and $f];
  7854. Inc(Result);
  7855. {Next byte}
  7856. Inc(LDataPtr);
  7857. end;
  7858. {Dump ASCII}
  7859. LDataPtr := PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader));
  7860. for LByteNum := 0 to 255 do
  7861. begin
  7862. if LByteNum and 31 = 0 then
  7863. begin
  7864. Result^ := #13;
  7865. Inc(Result);
  7866. Result^ := #10;
  7867. Inc(Result);
  7868. end
  7869. else
  7870. begin
  7871. Result^ := ' ';
  7872. Inc(Result);
  7873. Result^ := ' ';
  7874. Inc(Result);
  7875. end;
  7876. {Set the hex data}
  7877. LVal := Byte(LDataPtr^);
  7878. if LVal < 32 then
  7879. Result^ := '.'
  7880. else
  7881. Result^ := AnsiChar(LVal);
  7882. Inc(Result);
  7883. {Next byte}
  7884. Inc(LDataPtr);
  7885. end;
  7886. end;
  7887. {$endif}
  7888. {Rotates AValue ABitCount bits to the right}
  7889. function RotateRight(AValue, ABitCount: NativeUInt): NativeUInt;
  7890. asm
  7891. {$ifdef 32Bit}
  7892. mov ecx, edx
  7893. ror eax, cl
  7894. {$else}
  7895. mov rax, rcx
  7896. mov rcx, rdx
  7897. ror rax, cl
  7898. {$endif}
  7899. end;
  7900. {Determines whether a byte in the user portion of the freed block has been modified. Does not work beyond
  7901. the end of the user portion (i.e. footer and beyond).}
  7902. function FreeBlockByteWasModified(APointer: PFullDebugBlockHeader; AUserOffset: NativeUInt): Boolean;
  7903. var
  7904. LFillPattern: NativeUInt;
  7905. begin
  7906. {Get the expected fill pattern}
  7907. if AUserOffset < SizeOf(Pointer) then
  7908. begin
  7909. LFillPattern := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
  7910. end
  7911. else
  7912. begin
  7913. {$ifndef CatchUseOfFreedInterfaces}
  7914. LFillPattern := DebugFillPattern;
  7915. {$else}
  7916. LFillPattern := NativeUInt(@VMTBadInterface);
  7917. {$endif}
  7918. end;
  7919. {Compare the byte value}
  7920. Result := Byte(PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader) + AUserOffset)^) <>
  7921. Byte(RotateRight(LFillPattern, (AUserOffset and (SizeOf(Pointer) - 1)) * 8));
  7922. end;
  7923. function LogBlockChanges(APointer: PFullDebugBlockHeader; ABuffer: PAnsiChar): PAnsiChar;
  7924. var
  7925. LOffset, LChangeStart, LCount: NativeUInt;
  7926. LLogCount: Integer;
  7927. begin
  7928. {No errors logged so far}
  7929. LLogCount := 0;
  7930. {Log a maximum of 32 changes}
  7931. LOffset := 0;
  7932. while (LOffset < APointer.UserSize) and (LLogCount < 32) do
  7933. begin
  7934. {Has the byte been modified?}
  7935. if FreeBlockByteWasModified(APointer, LOffset) then
  7936. begin
  7937. {Found the start of a changed block, now find the length}
  7938. LChangeStart := LOffset;
  7939. LCount := 0;
  7940. while True do
  7941. begin
  7942. Inc(LCount);
  7943. Inc(LOffset);
  7944. if (LOffset >= APointer.UserSize)
  7945. or (not FreeBlockByteWasModified(APointer, LOffset)) then
  7946. begin
  7947. Break;
  7948. end;
  7949. end;
  7950. {Got the offset and length, now log it.}
  7951. if LLogCount = 0 then
  7952. begin
  7953. ABuffer := AppendStringToBuffer(FreeModifiedDetailMsg, ABuffer, Length(FreeModifiedDetailMsg));
  7954. end
  7955. else
  7956. begin
  7957. ABuffer^ := ',';
  7958. Inc(ABuffer);
  7959. ABuffer^ := ' ';
  7960. Inc(ABuffer);
  7961. end;
  7962. ABuffer := NativeUIntToStrBuf(LChangeStart, ABuffer);
  7963. ABuffer^ := '(';
  7964. Inc(ABuffer);
  7965. ABuffer := NativeUIntToStrBuf(LCount, ABuffer);
  7966. ABuffer^ := ')';
  7967. Inc(ABuffer);
  7968. {Increment the log count}
  7969. Inc(LLogCount);
  7970. end;
  7971. {Next byte}
  7972. Inc(LOffset);
  7973. end;
  7974. {Return the current buffer position}
  7975. Result := ABuffer;
  7976. end;
  7977. procedure LogBlockError(APointer: PFullDebugBlockHeader; AOperation: TBlockOperation; LHeaderValid, LFooterValid: Boolean);
  7978. var
  7979. LMsgPtr: PAnsiChar;
  7980. LErrorMessage: array[0..32767] of AnsiChar;
  7981. {$ifndef NoMessageBoxes}
  7982. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7983. {$endif}
  7984. LClass: TClass;
  7985. {$ifdef CheckCppObjectTypeEnabled}
  7986. LCppObjectTypeName: PAnsiChar;
  7987. {$endif}
  7988. begin
  7989. {Display the error header and the operation type.}
  7990. LMsgPtr := AppendStringToBuffer(ErrorMsgHeader, @LErrorMessage[0], Length(ErrorMsgHeader));
  7991. case AOperation of
  7992. boGetMem: LMsgPtr := AppendStringToBuffer(GetMemMsg, LMsgPtr, Length(GetMemMsg));
  7993. boFreeMem: LMsgPtr := AppendStringToBuffer(FreeMemMsg, LMsgPtr, Length(FreeMemMsg));
  7994. boReallocMem: LMsgPtr := AppendStringToBuffer(ReallocMemMsg, LMsgPtr, Length(ReallocMemMsg));
  7995. boBlockCheck: LMsgPtr := AppendStringToBuffer(BlockCheckMsg, LMsgPtr, Length(BlockCheckMsg));
  7996. end;
  7997. LMsgPtr := AppendStringToBuffer(OperationMsg, LMsgPtr, Length(OperationMsg));
  7998. {Is the header still intact?}
  7999. if LHeaderValid then
  8000. begin
  8001. {Is the footer still valid?}
  8002. if LFooterValid then
  8003. begin
  8004. {A freed block has been modified, a double free has occurred, or an
  8005. attempt was made to free a memory block allocated by a different
  8006. instance of FastMM.}
  8007. if AOperation <= boGetMem then
  8008. begin
  8009. LMsgPtr := AppendStringToBuffer(FreeModifiedErrorMsg, LMsgPtr, Length(FreeModifiedErrorMsg));
  8010. {Log the exact changes that caused the error.}
  8011. LMsgPtr := LogBlockChanges(APointer, LMsgPtr);
  8012. end
  8013. else
  8014. begin
  8015. {It is either a double free, or an attempt was made to free a block
  8016. that was allocated via a different memory manager.}
  8017. if APointer.AllocatedByRoutine = nil then
  8018. LMsgPtr := AppendStringToBuffer(DoubleFreeErrorMsg, LMsgPtr, Length(DoubleFreeErrorMsg))
  8019. else
  8020. LMsgPtr := AppendStringToBuffer(WrongMMFreeErrorMsg, LMsgPtr, Length(WrongMMFreeErrorMsg));
  8021. end;
  8022. end
  8023. else
  8024. begin
  8025. LMsgPtr := AppendStringToBuffer(BlockFooterCorruptedMsg, LMsgPtr, Length(BlockFooterCorruptedMsg))
  8026. end;
  8027. {Set the block size message}
  8028. if AOperation <= boGetMem then
  8029. LMsgPtr := AppendStringToBuffer(PreviousBlockSizeMsg, LMsgPtr, Length(PreviousBlockSizeMsg))
  8030. else
  8031. LMsgPtr := AppendStringToBuffer(CurrentBlockSizeMsg, LMsgPtr, Length(CurrentBlockSizeMsg));
  8032. LMsgPtr := NativeUIntToStrBuf(APointer.UserSize, LMsgPtr);
  8033. {The header is still intact - display info about the this/previous allocation}
  8034. if APointer.AllocationStackTrace[0] <> 0 then
  8035. begin
  8036. if AOperation <= boGetMem then
  8037. LMsgPtr := AppendStringToBuffer(ThreadIDPrevAllocMsg, LMsgPtr, Length(ThreadIDPrevAllocMsg))
  8038. else
  8039. LMsgPtr := AppendStringToBuffer(ThreadIDAtAllocMsg, LMsgPtr, Length(ThreadIDAtAllocMsg));
  8040. LMsgPtr := NativeUIntToHexBuf(APointer.AllocatedByThread, LMsgPtr);
  8041. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  8042. LMsgPtr := LogStackTrace(@APointer.AllocationStackTrace, StackTraceDepth, LMsgPtr);
  8043. end;
  8044. {Get the class this block was used for previously}
  8045. LClass := DetectClassInstance(@APointer.PreviouslyUsedByClass);
  8046. if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
  8047. begin
  8048. LMsgPtr := AppendStringToBuffer(PreviousObjectClassMsg, LMsgPtr, Length(PreviousObjectClassMsg));
  8049. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8050. end;
  8051. {$ifdef CheckCppObjectTypeEnabled}
  8052. if (LClass = nil) and Assigned(GetCppVirtObjTypeNameByVTablePtrFunc) then
  8053. begin
  8054. LCppObjectTypeName := GetCppVirtObjTypeNameByVTablePtrFunc(Pointer(APointer.PreviouslyUsedByClass), 0);
  8055. if Assigned(LCppObjectTypeName) then
  8056. begin
  8057. LMsgPtr := AppendStringToBuffer(PreviousObjectClassMsg, LMsgPtr, Length(PreviousObjectClassMsg));
  8058. LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName));
  8059. end;
  8060. end;
  8061. {$endif}
  8062. {Get the current class for this block}
  8063. if (AOperation > boGetMem) and (APointer.AllocatedByRoutine <> nil) then
  8064. begin
  8065. LMsgPtr := AppendStringToBuffer(CurrentObjectClassMsg, LMsgPtr, Length(CurrentObjectClassMsg));
  8066. LClass := DetectClassInstance(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)));
  8067. if IntPtr(LClass) = IntPtr(@FreedObjectVMT.VMTMethods[0]) then
  8068. LClass := nil;
  8069. {$ifndef CheckCppObjectTypeEnabled}
  8070. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8071. {$else}
  8072. if (LClass = nil) and Assigned(GetCppVirtObjTypeNameFunc) then
  8073. begin
  8074. LCppObjectTypeName := GetCppVirtObjTypeNameFunc(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)),
  8075. APointer.UserSize);
  8076. if LCppObjectTypeName <> nil then
  8077. LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName))
  8078. else
  8079. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8080. end
  8081. else
  8082. begin
  8083. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8084. end;
  8085. {$endif}
  8086. {Log the allocation group}
  8087. if APointer.AllocationGroup > 0 then
  8088. begin
  8089. LMsgPtr := AppendStringToBuffer(CurrentAllocationGroupMsg, LMsgPtr, Length(CurrentAllocationGroupMsg));
  8090. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
  8091. end;
  8092. {Log the allocation number}
  8093. LMsgPtr := AppendStringToBuffer(CurrentAllocationNumberMsg, LMsgPtr, Length(CurrentAllocationNumberMsg));
  8094. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
  8095. end
  8096. else
  8097. begin
  8098. {Log the allocation group}
  8099. if APointer.AllocationGroup > 0 then
  8100. begin
  8101. LMsgPtr := AppendStringToBuffer(PreviousAllocationGroupMsg, LMsgPtr, Length(PreviousAllocationGroupMsg));
  8102. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
  8103. end;
  8104. {Log the allocation number}
  8105. LMsgPtr := AppendStringToBuffer(PreviousAllocationNumberMsg, LMsgPtr, Length(PreviousAllocationNumberMsg));
  8106. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
  8107. end;
  8108. {Get the call stack for the previous free}
  8109. if APointer.FreeStackTrace[0] <> 0 then
  8110. begin
  8111. LMsgPtr := AppendStringToBuffer(ThreadIDAtFreeMsg, LMsgPtr, Length(ThreadIDAtFreeMsg));
  8112. LMsgPtr := NativeUIntToHexBuf(APointer.FreedByThread, LMsgPtr);
  8113. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  8114. LMsgPtr := LogStackTrace(@APointer.FreeStackTrace, StackTraceDepth, LMsgPtr);
  8115. end;
  8116. end
  8117. else
  8118. begin
  8119. {Header has been corrupted}
  8120. LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedMsg, LMsgPtr, Length(BlockHeaderCorruptedMsg));
  8121. end;
  8122. {Add the current stack trace}
  8123. LMsgPtr := LogCurrentThreadAndStackTrace(3 + Ord(AOperation <> boGetMem) + Ord(AOperation = boReallocMem), LMsgPtr);
  8124. {$ifndef DisableLoggingOfMemoryDumps}
  8125. {Add the memory dump}
  8126. LMsgPtr := LogMemoryDump(APointer, LMsgPtr);
  8127. {$endif}
  8128. {Trailing CRLF}
  8129. LMsgPtr^ := #13;
  8130. Inc(LMsgPtr);
  8131. LMsgPtr^ := #10;
  8132. Inc(LMsgPtr);
  8133. {Trailing #0}
  8134. LMsgPtr^ := #0;
  8135. {$ifdef LogErrorsToFile}
  8136. {Log the error}
  8137. AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
  8138. {$endif}
  8139. {$ifdef UseOutputDebugString}
  8140. OutputDebugStringA(LErrorMessage);
  8141. {$endif}
  8142. {Show the message}
  8143. {$ifndef NoMessageBoxes}
  8144. AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
  8145. ShowMessageBox(LErrorMessage, LErrorMessageTitle);
  8146. {$endif}
  8147. end;
  8148. {Logs the stack traces for a memory leak to file}
  8149. procedure LogMemoryLeakOrAllocatedBlock(APointer: PFullDebugBlockHeader; IsALeak: Boolean);
  8150. var
  8151. LHeaderValid: Boolean;
  8152. LMsgPtr: PAnsiChar;
  8153. LErrorMessage: array[0..32767] of AnsiChar;
  8154. LClass: TClass;
  8155. {$ifdef CheckCppObjectTypeEnabled}
  8156. LCppObjectTypeName: PAnsiChar;
  8157. {$endif}
  8158. begin
  8159. {Display the error header and the operation type.}
  8160. if IsALeak then
  8161. LMsgPtr := AppendStringToBuffer(LeakLogHeader, @LErrorMessage[0], Length(LeakLogHeader))
  8162. else
  8163. LMsgPtr := AppendStringToBuffer(BlockScanLogHeader, @LErrorMessage[0], Length(BlockScanLogHeader));
  8164. LMsgPtr := NativeUIntToStrBuf(GetAvailableSpaceInBlock(APointer) - FullDebugBlockOverhead, LMsgPtr);
  8165. {Is the debug info surrounding the block valid?}
  8166. LHeaderValid := CalculateHeaderCheckSum(APointer) = APointer.HeaderCheckSum;
  8167. {Is the header still intact?}
  8168. if LHeaderValid then
  8169. begin
  8170. {The header is still intact - display info about this/previous allocation}
  8171. if APointer.AllocationStackTrace[0] <> 0 then
  8172. begin
  8173. LMsgPtr := AppendStringToBuffer(ThreadIDAtAllocMsg, LMsgPtr, Length(ThreadIDAtAllocMsg));
  8174. LMsgPtr := NativeUIntToHexBuf(APointer.AllocatedByThread, LMsgPtr);
  8175. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  8176. LMsgPtr := LogStackTrace(@APointer.AllocationStackTrace, StackTraceDepth, LMsgPtr);
  8177. end;
  8178. LMsgPtr := AppendStringToBuffer(CurrentObjectClassMsg, LMsgPtr, Length(CurrentObjectClassMsg));
  8179. {Get the current class for this block}
  8180. LClass := DetectClassInstance(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)));
  8181. if IntPtr(LClass) = IntPtr(@FreedObjectVMT.VMTMethods[0]) then
  8182. LClass := nil;
  8183. {$ifndef CheckCppObjectTypeEnabled}
  8184. if LClass <> nil then
  8185. begin
  8186. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8187. end
  8188. else
  8189. begin
  8190. case DetectStringData(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)), APointer.UserSize) of
  8191. stUnknown: LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
  8192. stAnsiString: LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
  8193. stUnicodeString: LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
  8194. end;
  8195. end;
  8196. {$else}
  8197. if (LClass = nil) and Assigned(GetCppVirtObjTypeNameFunc) then
  8198. begin
  8199. LCppObjectTypeName := GetCppVirtObjTypeNameFunc(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)),
  8200. APointer.UserSize);
  8201. if LCppObjectTypeName <> nil then
  8202. LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName))
  8203. else
  8204. begin
  8205. case DetectStringData(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)), APointer.UserSize) of
  8206. stUnknown: LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
  8207. stAnsiString: LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
  8208. stUnicodeString: LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
  8209. end;
  8210. end;
  8211. end
  8212. else
  8213. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8214. {$endif}
  8215. {Log the allocation group}
  8216. if APointer.AllocationGroup > 0 then
  8217. begin
  8218. LMsgPtr := AppendStringToBuffer(CurrentAllocationGroupMsg, LMsgPtr, Length(CurrentAllocationGroupMsg));
  8219. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
  8220. end;
  8221. {Log the allocation number}
  8222. LMsgPtr := AppendStringToBuffer(CurrentAllocationNumberMsg, LMsgPtr, Length(CurrentAllocationNumberMsg));
  8223. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
  8224. end
  8225. else
  8226. begin
  8227. {Header has been corrupted}
  8228. LMsgPtr^ := '.';
  8229. Inc(LMsgPtr);
  8230. LMsgPtr^ := ' ';
  8231. Inc(LMsgPtr);
  8232. LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedMsg, LMsgPtr, Length(BlockHeaderCorruptedMsg));
  8233. end;
  8234. {$ifndef DisableLoggingOfMemoryDumps}
  8235. {Add the memory dump}
  8236. LMsgPtr := LogMemoryDump(APointer, LMsgPtr);
  8237. {$endif}
  8238. {Trailing CRLF}
  8239. LMsgPtr^ := #13;
  8240. Inc(LMsgPtr);
  8241. LMsgPtr^ := #10;
  8242. Inc(LMsgPtr);
  8243. {Trailing #0}
  8244. LMsgPtr^ := #0;
  8245. {Log the error}
  8246. AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
  8247. end;
  8248. {Checks that a free block is unmodified}
  8249. function CheckFreeBlockUnmodified(APBlock: PFullDebugBlockHeader; ABlockSize: NativeUInt;
  8250. AOperation: TBlockOperation): Boolean;
  8251. var
  8252. LHeaderCheckSum: NativeUInt;
  8253. LHeaderValid, LFooterValid, LBlockUnmodified: Boolean;
  8254. begin
  8255. LHeaderCheckSum := CalculateHeaderCheckSum(APBlock);
  8256. LHeaderValid := LHeaderCheckSum = APBlock.HeaderCheckSum;
  8257. {Is the footer itself still in place}
  8258. LFooterValid := LHeaderValid
  8259. and (PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ = (not LHeaderCheckSum));
  8260. {Is the footer and debug VMT in place? The debug VMT is only valid if the user size is greater than the size of a pointer.}
  8261. if LFooterValid
  8262. and (APBlock.UserSize < SizeOf(Pointer)) or (PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader))^ = NativeUInt(@FreedObjectVMT.VMTMethods[0])) then
  8263. begin
  8264. {Store the debug fill pattern in place of the footer in order to simplify
  8265. checking for block modifications.}
  8266. PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ :=
  8267. {$ifndef CatchUseOfFreedInterfaces}
  8268. DebugFillPattern;
  8269. {$else}
  8270. RotateRight(NativeUInt(@VMTBadInterface), (APBlock.UserSize and (SizeOf(Pointer) - 1)) * 8);
  8271. {$endif}
  8272. {Check that all the filler bytes are valid inside the block, except for
  8273. the "dummy" class header}
  8274. LBlockUnmodified := CheckFillPattern(PNativeUInt(PByte(APBlock) + (SizeOf(TFullDebugBlockHeader) + SizeOf(Pointer))),
  8275. ABlockSize - (FullDebugBlockOverhead + SizeOf(Pointer)),
  8276. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  8277. {Reset the old footer}
  8278. PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ := not LHeaderCheckSum;
  8279. end
  8280. else
  8281. LBlockUnmodified := False;
  8282. if (not LHeaderValid) or (not LFooterValid) or (not LBlockUnmodified) then
  8283. begin
  8284. LogBlockError(APBlock, AOperation, LHeaderValid, LFooterValid);
  8285. Result := False;
  8286. end
  8287. else
  8288. Result := True;
  8289. end;
  8290. function DebugGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  8291. begin
  8292. {Scan the entire memory pool first?}
  8293. if FullDebugModeScanMemoryPoolBeforeEveryOperation then
  8294. ScanMemoryPoolForCorruptions;
  8295. {Enter the memory manager: block scans may not be performed now}
  8296. StartChangingFullDebugModeBlock;
  8297. try
  8298. {We need extra space for (a) The debug header, (b) the block debug trailer
  8299. and (c) the trailing block size pointer for free blocks}
  8300. Result := FastGetMem(ASize + FullDebugBlockOverhead);
  8301. if Result <> nil then
  8302. begin
  8303. {Large blocks are always newly allocated (and never reused), so checking
  8304. for a modify-after-free is not necessary.}
  8305. if (ASize > (MaximumMediumBlockSize - BlockHeaderSize - FullDebugBlockOverhead))
  8306. or CheckFreeBlockUnmodified(Result, GetAvailableSpaceInBlock(Result) + BlockHeaderSize, boGetMem) then
  8307. begin
  8308. {Set the allocation call stack}
  8309. GetStackTrace(@PFullDebugBlockHeader(Result).AllocationStackTrace, StackTraceDepth, 1);
  8310. {Set the thread ID of the thread that allocated the block}
  8311. PFullDebugBlockHeader(Result).AllocatedByThread := GetThreadID;
  8312. {Block is now in use: It was allocated by this routine}
  8313. PFullDebugBlockHeader(Result).AllocatedByRoutine := @DebugGetMem;
  8314. {Set the group number}
  8315. PFullDebugBlockHeader(Result).AllocationGroup := AllocationGroupStack[AllocationGroupStackTop];
  8316. {Set the allocation number}
  8317. IncrementAllocationNumber;
  8318. PFullDebugBlockHeader(Result).AllocationNumber := CurrentAllocationNumber;
  8319. {Clear the previous block trailer}
  8320. PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(Result).UserSize)^ :=
  8321. {$ifndef CatchUseOfFreedInterfaces}
  8322. DebugFillPattern;
  8323. {$else}
  8324. RotateRight(NativeUInt(@VMTBadInterface), (PFullDebugBlockHeader(Result).UserSize and (SizeOf(Pointer) - 1)) * 8);
  8325. {$endif}
  8326. {Set the user size for the block}
  8327. PFullDebugBlockHeader(Result).UserSize := ASize;
  8328. {Set the checksums}
  8329. UpdateHeaderAndFooterCheckSums(Result);
  8330. {$ifdef FullDebugModeCallBacks}
  8331. if Assigned(OnDebugGetMemFinish) then
  8332. OnDebugGetMemFinish(PFullDebugBlockHeader(Result), ASize);
  8333. {$endif}
  8334. {Return the start of the actual block}
  8335. Result := Pointer(PByte(Result) + SizeOf(TFullDebugBlockHeader));
  8336. {Should this block be marked as an expected leak automatically?}
  8337. if FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak then
  8338. RegisterExpectedMemoryLeak(Result);
  8339. end
  8340. else
  8341. begin
  8342. Result := nil;
  8343. end;
  8344. end;
  8345. finally
  8346. {Leaving the memory manager routine: Block scans may be performed again.}
  8347. DoneChangingFullDebugModeBlock;
  8348. end;
  8349. end;
  8350. function CheckBlockBeforeFreeOrRealloc(APBlock: PFullDebugBlockHeader;
  8351. AOperation: TBlockOperation): Boolean;
  8352. var
  8353. LHeaderValid, LFooterValid: Boolean;
  8354. LPFooter: PNativeUInt;
  8355. {$ifndef CatchUseOfFreedInterfaces}
  8356. LBlockSize: NativeUInt;
  8357. LPTrailingByte, LPFillPatternEnd: PByte;
  8358. {$endif}
  8359. begin
  8360. {Is the checksum for the block header valid?}
  8361. LHeaderValid := CalculateHeaderCheckSum(APBlock) = APBlock.HeaderCheckSum;
  8362. {If the header is corrupted then the footer is assumed to be corrupt too.}
  8363. if LHeaderValid then
  8364. begin
  8365. {Check the footer checksum: The footer checksum should equal the header
  8366. checksum with all bits inverted.}
  8367. LPFooter := PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(APBlock).UserSize);
  8368. if APBlock.HeaderCheckSum = (not (LPFooter^)) then
  8369. begin
  8370. LFooterValid := True;
  8371. {$ifndef CatchUseOfFreedInterfaces}
  8372. {Large blocks do not have the debug fill pattern, since they are never reused.}
  8373. if PNativeUInt(PByte(APBlock) - BlockHeaderSize)^ and (IsMediumBlockFlag or IsLargeBlockFlag) <> IsLargeBlockFlag then
  8374. begin
  8375. {Check that the application has not modified bytes beyond the block
  8376. footer. The $80 fill pattern should extend up to 2 nativeints before
  8377. the start of the next block (leaving space for the free block size and
  8378. next block header.)}
  8379. LBlockSize := GetAvailableSpaceInBlock(APBlock);
  8380. LPFillPatternEnd := PByte(PByte(APBlock) + LBlockSize - SizeOf(Pointer));
  8381. LPTrailingByte := PByte(PByte(LPFooter) + SizeOf(NativeUInt));
  8382. while UIntPtr(LPTrailingByte) < UIntPtr(LPFillPatternEnd) do
  8383. begin
  8384. if Byte(LPTrailingByte^) <> DebugFillByte then
  8385. begin
  8386. LFooterValid := False;
  8387. Break;
  8388. end;
  8389. Inc(LPTrailingByte);
  8390. end;
  8391. end;
  8392. {$endif}
  8393. end
  8394. else
  8395. LFooterValid := False;
  8396. end
  8397. else
  8398. LFooterValid := False;
  8399. {The header and footer must be intact and the block must have been allocated
  8400. by this memory manager instance.}
  8401. if LFooterValid and (APBlock.AllocatedByRoutine = @DebugGetMem) then
  8402. begin
  8403. Result := True;
  8404. end
  8405. else
  8406. begin
  8407. {Log the error}
  8408. LogBlockError(APBlock, AOperation, LHeaderValid, LFooterValid);
  8409. {Return an error}
  8410. Result := False;
  8411. end;
  8412. end;
  8413. function DebugFreeMem(APointer: Pointer): Integer;
  8414. var
  8415. LActualBlock: PFullDebugBlockHeader;
  8416. LBlockHeader: NativeUInt;
  8417. begin
  8418. {Scan the entire memory pool first?}
  8419. if FullDebugModeScanMemoryPoolBeforeEveryOperation then
  8420. ScanMemoryPoolForCorruptions;
  8421. {Get a pointer to the start of the actual block}
  8422. LActualBlock := PFullDebugBlockHeader(PByte(APointer)
  8423. - SizeOf(TFullDebugBlockHeader));
  8424. {Is the debug info surrounding the block valid?}
  8425. if CheckBlockBeforeFreeOrRealloc(LActualBlock, boFreeMem) then
  8426. begin
  8427. {Enter the memory manager: block scans may not be performed now}
  8428. StartChangingFullDebugModeBlock;
  8429. try
  8430. {$ifdef FullDebugModeCallBacks}
  8431. if Assigned(OnDebugFreeMemStart) then
  8432. OnDebugFreeMemStart(LActualBlock);
  8433. {$endif}
  8434. {Large blocks are never reused, so there is no point in updating their
  8435. headers and fill pattern.}
  8436. LBlockHeader := PNativeUInt(PByte(LActualBlock) - BlockHeaderSize)^;
  8437. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) <> IsLargeBlockFlag then
  8438. begin
  8439. {Get the class the block was used for}
  8440. LActualBlock.PreviouslyUsedByClass := PNativeUInt(APointer)^;
  8441. {Set the free call stack}
  8442. GetStackTrace(@LActualBlock.FreeStackTrace, StackTraceDepth, 1);
  8443. {Set the thread ID of the thread that freed the block}
  8444. LActualBlock.FreedByThread := GetThreadID;
  8445. {Block is now free}
  8446. LActualBlock.AllocatedByRoutine := nil;
  8447. {Clear the user area of the block}
  8448. DebugFillMem(APointer^, LActualBlock.UserSize,
  8449. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  8450. {Set a pointer to the dummy VMT}
  8451. PNativeUInt(APointer)^ := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
  8452. {Recalculate the checksums}
  8453. UpdateHeaderAndFooterCheckSums(LActualBlock);
  8454. end;
  8455. {Automatically deregister the expected memory leak?}
  8456. if FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak then
  8457. UnregisterExpectedMemoryLeak(APointer);
  8458. {Free the actual block}
  8459. Result := FastFreeMem(LActualBlock);
  8460. {$ifdef FullDebugModeCallBacks}
  8461. if Assigned(OnDebugFreeMemFinish) then
  8462. OnDebugFreeMemFinish(LActualBlock, Result);
  8463. {$endif}
  8464. finally
  8465. {Leaving the memory manager routine: Block scans may be performed again.}
  8466. DoneChangingFullDebugModeBlock;
  8467. end;
  8468. end
  8469. else
  8470. begin
  8471. {$ifdef SuppressFreeMemErrorsInsideException}
  8472. if ExceptObject <> nil then
  8473. Result := 0
  8474. else
  8475. {$endif}
  8476. Result := -1;
  8477. end;
  8478. end;
  8479. function DebugReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  8480. var
  8481. LMoveSize, LBlockSpace: NativeUInt;
  8482. LActualBlock, LNewActualBlock: PFullDebugBlockHeader;
  8483. begin
  8484. {Scan the entire memory pool first?}
  8485. if FullDebugModeScanMemoryPoolBeforeEveryOperation then
  8486. ScanMemoryPoolForCorruptions;
  8487. {Get a pointer to the start of the actual block}
  8488. LActualBlock := PFullDebugBlockHeader(PByte(APointer)
  8489. - SizeOf(TFullDebugBlockHeader));
  8490. {Is the debug info surrounding the block valid?}
  8491. if CheckBlockBeforeFreeOrRealloc(LActualBlock, boReallocMem) then
  8492. begin
  8493. {Get the current block size}
  8494. LBlockSpace := GetAvailableSpaceInBlock(LActualBlock);
  8495. {Can the block fit? We need space for the debug overhead and the block header
  8496. of the next block}
  8497. if LBlockSpace < (NativeUInt(ANewSize) + FullDebugBlockOverhead) then
  8498. begin
  8499. {Get a new block of the requested size.}
  8500. Result := DebugGetMem(ANewSize);
  8501. if Result <> nil then
  8502. begin
  8503. {Block scans may not be performed now}
  8504. StartChangingFullDebugModeBlock;
  8505. try
  8506. {$ifdef FullDebugModeCallBacks}
  8507. if Assigned(OnDebugReallocMemStart) then
  8508. OnDebugReallocMemStart(LActualBlock, ANewSize);
  8509. {$endif}
  8510. {We reuse the old allocation number. Since DebugGetMem always bumps
  8511. CurrentAllocationGroup, there may be gaps in the sequence of
  8512. allocation numbers.}
  8513. LNewActualBlock := PFullDebugBlockHeader(PByte(Result)
  8514. - SizeOf(TFullDebugBlockHeader));
  8515. LNewActualBlock.AllocationGroup := LActualBlock.AllocationGroup;
  8516. LNewActualBlock.AllocationNumber := LActualBlock.AllocationNumber;
  8517. {Recalculate the header and footer checksums}
  8518. UpdateHeaderAndFooterCheckSums(LNewActualBlock);
  8519. {$ifdef FullDebugModeCallBacks}
  8520. if Assigned(OnDebugReallocMemFinish) then
  8521. OnDebugReallocMemFinish(LNewActualBlock, ANewSize);
  8522. {$endif}
  8523. finally
  8524. {Block scans can again be performed safely}
  8525. DoneChangingFullDebugModeBlock;
  8526. end;
  8527. {How many bytes to move?}
  8528. LMoveSize := LActualBlock.UserSize;
  8529. if LMoveSize > NativeUInt(ANewSize) then
  8530. LMoveSize := ANewSize;
  8531. {Move the data across}
  8532. System.Move(APointer^, Result^, LMoveSize);
  8533. {Free the old block}
  8534. DebugFreeMem(APointer);
  8535. end
  8536. else
  8537. begin
  8538. Result := nil;
  8539. end;
  8540. end
  8541. else
  8542. begin
  8543. {Block scans may not be performed now}
  8544. StartChangingFullDebugModeBlock;
  8545. try
  8546. {$ifdef FullDebugModeCallBacks}
  8547. if Assigned(OnDebugReallocMemStart) then
  8548. OnDebugReallocMemStart(LActualBlock, ANewSize);
  8549. {$endif}
  8550. {Clear all data after the new end of the block up to the old end of the
  8551. block, including the trailer.}
  8552. DebugFillMem(Pointer(PByte(APointer) + NativeUInt(ANewSize) + SizeOf(NativeUInt))^,
  8553. NativeInt(LActualBlock.UserSize) - ANewSize,
  8554. {$ifndef CatchUseOfFreedInterfaces}
  8555. DebugFillPattern);
  8556. {$else}
  8557. RotateRight(NativeUInt(@VMTBadInterface), (ANewSize and (SizeOf(Pointer) - 1)) * 8));
  8558. {$endif}
  8559. {Update the user size}
  8560. LActualBlock.UserSize := ANewSize;
  8561. {Set the new checksums}
  8562. UpdateHeaderAndFooterCheckSums(LActualBlock);
  8563. {$ifdef FullDebugModeCallBacks}
  8564. if Assigned(OnDebugReallocMemFinish) then
  8565. OnDebugReallocMemFinish(LActualBlock, ANewSize);
  8566. {$endif}
  8567. finally
  8568. {Block scans can again be performed safely}
  8569. DoneChangingFullDebugModeBlock;
  8570. end;
  8571. {Return the old pointer}
  8572. Result := APointer;
  8573. end;
  8574. end
  8575. else
  8576. begin
  8577. Result := nil;
  8578. end;
  8579. end;
  8580. {Allocates a block and fills it with zeroes}
  8581. function DebugAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  8582. begin
  8583. Result := DebugGetMem(ASize);
  8584. {Clear the block}
  8585. if Result <> nil then
  8586. FillChar(Result^, ASize, 0);
  8587. end;
  8588. {Raises a runtime error if a memory corruption was encountered. Subroutine for
  8589. InternalScanMemoryPool and InternalScanSmallBlockPool.}
  8590. procedure RaiseMemoryCorruptionError;
  8591. begin
  8592. {Disable exhaustive checking in order to prevent recursive exceptions.}
  8593. FullDebugModeScanMemoryPoolBeforeEveryOperation := False;
  8594. {Unblock the memory manager in case the creation of the exception below
  8595. causes an attempt to be made to allocate memory.}
  8596. UnblockFullDebugModeMMRoutines;
  8597. {Raise the runtime error}
  8598. {$ifdef BCB6OrDelphi7AndUp}
  8599. System.Error(reOutOfMemory);
  8600. {$else}
  8601. System.RunError(reOutOfMemory);
  8602. {$endif}
  8603. end;
  8604. {Subroutine for InternalScanMemoryPool: Checks the given small block pool for
  8605. allocated blocks}
  8606. procedure InternalScanSmallBlockPool(APSmallBlockPool: PSmallBlockPoolHeader;
  8607. AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
  8608. var
  8609. LCurPtr, LEndPtr: Pointer;
  8610. begin
  8611. {Get the first and last pointer for the pool}
  8612. GetFirstAndLastSmallBlockInPool(APSmallBlockPool, LCurPtr, LEndPtr);
  8613. {Step through all blocks}
  8614. while UIntPtr(LCurPtr) <= UIntPtr(LEndPtr) do
  8615. begin
  8616. {Is this block in use? If so, is the debug info intact?}
  8617. if ((PNativeUInt(PByte(LCurPtr) - BlockHeaderSize)^ and IsFreeBlockFlag) = 0) then
  8618. begin
  8619. if CheckBlockBeforeFreeOrRealloc(LCurPtr, boBlockCheck) then
  8620. begin
  8621. if (PFullDebugBlockHeader(LCurPtr).AllocationGroup >= AFirstAllocationGroupToLog)
  8622. and (PFullDebugBlockHeader(LCurPtr).AllocationGroup <= ALastAllocationGroupToLog) then
  8623. begin
  8624. LogMemoryLeakOrAllocatedBlock(LCurPtr, False);
  8625. end;
  8626. end
  8627. else
  8628. RaiseMemoryCorruptionError;
  8629. end
  8630. else
  8631. begin
  8632. {Check that the block has not been modified since being freed}
  8633. if not CheckFreeBlockUnmodified(LCurPtr, APSmallBlockPool.BlockType.BlockSize, boBlockCheck) then
  8634. RaiseMemoryCorruptionError;
  8635. end;
  8636. {Next block}
  8637. Inc(PByte(LCurPtr), APSmallBlockPool.BlockType.BlockSize);
  8638. end;
  8639. end;
  8640. {Subroutine for LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions:
  8641. Scans the memory pool for corruptions and optionally logs allocated blocks
  8642. in the allocation group range.}
  8643. procedure InternalScanMemoryPool(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
  8644. var
  8645. LPLargeBlock: PLargeBlockHeader;
  8646. LPMediumBlock: Pointer;
  8647. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  8648. LMediumBlockHeader: NativeUInt;
  8649. begin
  8650. {Block all the memory manager routines while performing the scan. No memory
  8651. block may be allocated or freed, and no FullDebugMode block header or
  8652. footer may be modified, while the scan is in progress.}
  8653. BlockFullDebugModeMMRoutines;
  8654. try
  8655. {Step through all the medium block pools}
  8656. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  8657. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  8658. begin
  8659. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  8660. while LPMediumBlock <> nil do
  8661. begin
  8662. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  8663. {Is the block in use?}
  8664. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  8665. begin
  8666. {Block is in use: Is it a medium block or small block pool?}
  8667. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  8668. begin
  8669. {Get all the leaks for the small block pool}
  8670. InternalScanSmallBlockPool(LPMediumBlock, AFirstAllocationGroupToLog, ALastAllocationGroupToLog);
  8671. end
  8672. else
  8673. begin
  8674. if CheckBlockBeforeFreeOrRealloc(LPMediumBlock, boBlockCheck) then
  8675. begin
  8676. if (PFullDebugBlockHeader(LPMediumBlock).AllocationGroup >= AFirstAllocationGroupToLog)
  8677. and (PFullDebugBlockHeader(LPMediumBlock).AllocationGroup <= ALastAllocationGroupToLog) then
  8678. begin
  8679. LogMemoryLeakOrAllocatedBlock(LPMediumBlock, False);
  8680. end;
  8681. end
  8682. else
  8683. RaiseMemoryCorruptionError;
  8684. end;
  8685. end
  8686. else
  8687. begin
  8688. {Check that the block has not been modified since being freed}
  8689. if not CheckFreeBlockUnmodified(LPMediumBlock, LMediumBlockHeader and DropMediumAndLargeFlagsMask, boBlockCheck) then
  8690. RaiseMemoryCorruptionError;
  8691. end;
  8692. {Next medium block}
  8693. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  8694. end;
  8695. {Get the next medium block pool}
  8696. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  8697. end;
  8698. {Scan large blocks}
  8699. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  8700. while LPLargeBlock <> @LargeBlocksCircularList do
  8701. begin
  8702. if CheckBlockBeforeFreeOrRealloc(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), boBlockCheck) then
  8703. begin
  8704. if (PFullDebugBlockHeader(PByte(LPLargeBlock) + LargeBlockHeaderSize).AllocationGroup >= AFirstAllocationGroupToLog)
  8705. and (PFullDebugBlockHeader(PByte(LPLargeBlock) + LargeBlockHeaderSize).AllocationGroup <= ALastAllocationGroupToLog) then
  8706. begin
  8707. LogMemoryLeakOrAllocatedBlock(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), False);
  8708. end;
  8709. end
  8710. else
  8711. RaiseMemoryCorruptionError;
  8712. {Get the next large block}
  8713. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  8714. end;
  8715. finally
  8716. {Unblock the FullDebugMode memory manager routines.}
  8717. UnblockFullDebugModeMMRoutines;
  8718. end;
  8719. end;
  8720. {Logs detail about currently allocated memory blocks for the specified range of
  8721. allocation groups. if ALastAllocationGroupToLog is less than
  8722. AFirstAllocationGroupToLog or it is zero, then all allocation groups are
  8723. logged. This routine also checks the memory pool for consistency at the same
  8724. time, raising an "Out of Memory" error if the check fails.}
  8725. procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
  8726. begin
  8727. {Validate input}
  8728. if (ALastAllocationGroupToLog = 0) or (ALastAllocationGroupToLog < AFirstAllocationGroupToLog) then
  8729. begin
  8730. {Bad input: log all groups}
  8731. AFirstAllocationGroupToLog := 0;
  8732. ALastAllocationGroupToLog := $ffffffff;
  8733. end;
  8734. {Scan the memory pool, logging allocated blocks in the requested range.}
  8735. InternalScanMemoryPool(AFirstAllocationGroupToLog, ALastAllocationGroupToLog);
  8736. end;
  8737. {Scans the memory pool for any corruptions. If a corruption is encountered an "Out of Memory" exception is
  8738. raised.}
  8739. procedure ScanMemoryPoolForCorruptions;
  8740. begin
  8741. {Scan the memory pool for corruptions, but don't log any allocated blocks}
  8742. InternalScanMemoryPool($ffffffff, 0);
  8743. end;
  8744. {-----------------------Invalid Virtual Method Calls-------------------------}
  8745. { TFreedObject }
  8746. {Used to determine the index of the virtual method call on the freed object.
  8747. Do not change this without updating MaxFakeVMTEntries. Currently 200.}
  8748. procedure TFreedObject.GetVirtualMethodIndex;
  8749. asm
  8750. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8751. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8752. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8753. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8754. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8755. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8756. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8757. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8758. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8759. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8760. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8761. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8762. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8763. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8764. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8765. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8766. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8767. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8768. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8769. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8770. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8771. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8772. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8773. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8774. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8775. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8776. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8777. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8778. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8779. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8780. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8781. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8782. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8783. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8784. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8785. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8786. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8787. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8788. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8789. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8790. jmp TFreedObject.VirtualMethodError
  8791. end;
  8792. procedure TFreedObject.VirtualMethodError;
  8793. var
  8794. LVMOffset: Integer;
  8795. LMsgPtr: PAnsiChar;
  8796. LErrorMessage: array[0..32767] of AnsiChar;
  8797. {$ifndef NoMessageBoxes}
  8798. LErrorMessageTitle: array[0..1023] of AnsiChar;
  8799. {$endif}
  8800. LClass: TClass;
  8801. LActualBlock: PFullDebugBlockHeader;
  8802. begin
  8803. {Get the offset of the virtual method}
  8804. LVMOffset := (MaxFakeVMTEntries - VMIndex) * SizeOf(Pointer) + vmtParent + SizeOf(Pointer);
  8805. {Reset the index for the next error}
  8806. VMIndex := 0;
  8807. {Get the address of the actual block}
  8808. LActualBlock := PFullDebugBlockHeader(PByte(Self) - SizeOf(TFullDebugBlockHeader));
  8809. {Display the error header}
  8810. LMsgPtr := AppendStringToBuffer(VirtualMethodErrorHeader, @LErrorMessage[0], Length(VirtualMethodErrorHeader));
  8811. {Is the debug info surrounding the block valid?}
  8812. if CalculateHeaderCheckSum(LActualBlock) = LActualBlock.HeaderCheckSum then
  8813. begin
  8814. {Get the class this block was used for previously}
  8815. LClass := DetectClassInstance(@LActualBlock.PreviouslyUsedByClass);
  8816. if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
  8817. begin
  8818. LMsgPtr := AppendStringToBuffer(FreedObjectClassMsg, LMsgPtr, Length(FreedObjectClassMsg));
  8819. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8820. end;
  8821. {Get the virtual method name}
  8822. LMsgPtr := AppendStringToBuffer(VirtualMethodName, LMsgPtr, Length(VirtualMethodName));
  8823. if LVMOffset < 0 then
  8824. begin
  8825. LMsgPtr := AppendStringToBuffer(StandardVirtualMethodNames[LVMOffset div SizeOf(Pointer)], LMsgPtr, Length(StandardVirtualMethodNames[LVMOffset div SizeOf(Pointer)]));
  8826. end
  8827. else
  8828. begin
  8829. LMsgPtr := AppendStringToBuffer(VirtualMethodOffset, LMsgPtr, Length(VirtualMethodOffset));
  8830. LMsgPtr := NativeUIntToStrBuf(LVMOffset, LMsgPtr);
  8831. end;
  8832. {Virtual method address}
  8833. if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
  8834. begin
  8835. LMsgPtr := AppendStringToBuffer(VirtualMethodAddress, LMsgPtr, Length(VirtualMethodAddress));
  8836. LMsgPtr := NativeUIntToHexBuf(PNativeUInt(PByte(LClass) + LVMOffset)^, LMsgPtr);
  8837. end;
  8838. {Log the allocation group}
  8839. if LActualBlock.AllocationGroup > 0 then
  8840. begin
  8841. LMsgPtr := AppendStringToBuffer(PreviousAllocationGroupMsg, LMsgPtr, Length(PreviousAllocationGroupMsg));
  8842. LMsgPtr := NativeUIntToStrBuf(LActualBlock.AllocationGroup, LMsgPtr);
  8843. end;
  8844. {Log the allocation number}
  8845. LMsgPtr := AppendStringToBuffer(PreviousAllocationNumberMsg, LMsgPtr, Length(PreviousAllocationNumberMsg));
  8846. LMsgPtr := NativeUIntToStrBuf(LActualBlock.AllocationNumber, LMsgPtr);
  8847. {The header is still intact - display info about the this/previous allocation}
  8848. if LActualBlock.AllocationStackTrace[0] <> 0 then
  8849. begin
  8850. LMsgPtr := AppendStringToBuffer(ThreadIDAtObjectAllocMsg, LMsgPtr, Length(ThreadIDAtObjectAllocMsg));
  8851. LMsgPtr := NativeUIntToHexBuf(LActualBlock.AllocatedByThread, LMsgPtr);
  8852. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  8853. LMsgPtr := LogStackTrace(@LActualBlock.AllocationStackTrace, StackTraceDepth, LMsgPtr);
  8854. end;
  8855. {Get the call stack for the previous free}
  8856. if LActualBlock.FreeStackTrace[0] <> 0 then
  8857. begin
  8858. LMsgPtr := AppendStringToBuffer(ThreadIDAtObjectFreeMsg, LMsgPtr, Length(ThreadIDAtObjectFreeMsg));
  8859. LMsgPtr := NativeUIntToHexBuf(LActualBlock.FreedByThread, LMsgPtr);
  8860. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  8861. LMsgPtr := LogStackTrace(@LActualBlock.FreeStackTrace, StackTraceDepth, LMsgPtr);
  8862. end;
  8863. end
  8864. else
  8865. begin
  8866. {Header has been corrupted}
  8867. LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedNoHistoryMsg, LMsgPtr, Length(BlockHeaderCorruptedNoHistoryMsg));
  8868. end;
  8869. {Add the current stack trace}
  8870. LMsgPtr := LogCurrentThreadAndStackTrace(2, LMsgPtr);
  8871. {$ifndef DisableLoggingOfMemoryDumps}
  8872. {Add the pointer address}
  8873. LMsgPtr := LogMemoryDump(LActualBlock, LMsgPtr);
  8874. {$endif}
  8875. {Trailing CRLF}
  8876. LMsgPtr^ := #13;
  8877. Inc(LMsgPtr);
  8878. LMsgPtr^ := #10;
  8879. Inc(LMsgPtr);
  8880. {Trailing #0}
  8881. LMsgPtr^ := #0;
  8882. {$ifdef LogErrorsToFile}
  8883. {Log the error}
  8884. AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
  8885. {$endif}
  8886. {$ifdef UseOutputDebugString}
  8887. OutputDebugStringA(LErrorMessage);
  8888. {$endif}
  8889. {$ifndef NoMessageBoxes}
  8890. {Show the message}
  8891. AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
  8892. ShowMessageBox(LErrorMessage, LErrorMessageTitle);
  8893. {$endif}
  8894. {Raise an access violation}
  8895. RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nil);
  8896. end;
  8897. {$ifdef CatchUseOfFreedInterfaces}
  8898. procedure TFreedObject.InterfaceError;
  8899. var
  8900. LMsgPtr: PAnsiChar;
  8901. {$ifndef NoMessageBoxes}
  8902. LErrorMessageTitle: array[0..1023] of AnsiChar;
  8903. {$endif}
  8904. LErrorMessage: array[0..4000] of AnsiChar;
  8905. begin
  8906. {Display the error header}
  8907. LMsgPtr := AppendStringToBuffer(InterfaceErrorHeader, @LErrorMessage[0], Length(InterfaceErrorHeader));
  8908. {Add the current stack trace}
  8909. LMsgPtr := LogCurrentThreadAndStackTrace(2, LMsgPtr);
  8910. {Trailing CRLF}
  8911. LMsgPtr^ := #13;
  8912. Inc(LMsgPtr);
  8913. LMsgPtr^ := #10;
  8914. Inc(LMsgPtr);
  8915. {Trailing #0}
  8916. LMsgPtr^ := #0;
  8917. {$ifdef LogErrorsToFile}
  8918. {Log the error}
  8919. AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
  8920. {$endif}
  8921. {$ifdef UseOutputDebugString}
  8922. OutputDebugStringA(LErrorMessage);
  8923. {$endif}
  8924. {$ifndef NoMessageBoxes}
  8925. {Show the message}
  8926. AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
  8927. ShowMessageBox(LErrorMessage, LErrorMessageTitle);
  8928. {$endif}
  8929. {Raise an access violation}
  8930. RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nil);
  8931. end;
  8932. {$endif}
  8933. {$endif}
  8934. {----------------------------Memory Leak Checking-----------------------------}
  8935. {$ifdef EnableMemoryLeakReporting}
  8936. {Adds a leak to the specified list}
  8937. function UpdateExpectedLeakList(APLeakList: PPExpectedMemoryLeak;
  8938. APNewEntry: PExpectedMemoryLeak; AExactSizeMatch: Boolean = True): Boolean;
  8939. var
  8940. LPInsertAfter, LPNewEntry: PExpectedMemoryLeak;
  8941. begin
  8942. {Default to error}
  8943. Result := False;
  8944. {Find the insertion spot}
  8945. LPInsertAfter := APLeakList^;
  8946. while LPInsertAfter <> nil do
  8947. begin
  8948. {Too big?}
  8949. if LPInsertAfter.LeakSize > APNewEntry.LeakSize then
  8950. begin
  8951. LPInsertAfter := LPInsertAfter.PreviousLeak;
  8952. Break;
  8953. end;
  8954. {Find a matching entry. If an exact size match is not required and the leak
  8955. is larger than the current entry, use it if the expected size of the next
  8956. entry is too large.}
  8957. if (IntPtr(LPInsertAfter.LeakAddress) = IntPtr(APNewEntry.LeakAddress))
  8958. and ((IntPtr(LPInsertAfter.LeakedClass) = IntPtr(APNewEntry.LeakedClass))
  8959. {$ifdef CheckCppObjectTypeEnabled}
  8960. or (LPInsertAfter.LeakedCppTypeIdPtr = APNewEntry.LeakedCppTypeIdPtr)
  8961. {$endif}
  8962. )
  8963. and ((LPInsertAfter.LeakSize = APNewEntry.LeakSize)
  8964. or ((not AExactSizeMatch)
  8965. and (LPInsertAfter.LeakSize < APNewEntry.LeakSize)
  8966. and ((LPInsertAfter.NextLeak = nil)
  8967. or (LPInsertAfter.NextLeak.LeakSize > APNewEntry.LeakSize))
  8968. )) then
  8969. begin
  8970. if (LPInsertAfter.LeakCount + APNewEntry.LeakCount) >= 0 then
  8971. begin
  8972. Inc(LPInsertAfter.LeakCount, APNewEntry.LeakCount);
  8973. {Is the count now 0?}
  8974. if LPInsertAfter.LeakCount = 0 then
  8975. begin
  8976. {Delete the entry}
  8977. if LPInsertAfter.NextLeak <> nil then
  8978. LPInsertAfter.NextLeak.PreviousLeak := LPInsertAfter.PreviousLeak;
  8979. if LPInsertAfter.PreviousLeak <> nil then
  8980. LPInsertAfter.PreviousLeak.NextLeak := LPInsertAfter.NextLeak
  8981. else
  8982. APLeakList^ := LPInsertAfter.NextLeak;
  8983. {Insert it as the first free slot}
  8984. LPInsertAfter.NextLeak := ExpectedMemoryLeaks.FirstFreeSlot;
  8985. ExpectedMemoryLeaks.FirstFreeSlot := LPInsertAfter;
  8986. end;
  8987. Result := True;
  8988. end;
  8989. Exit;
  8990. end;
  8991. {Next entry}
  8992. if LPInsertAfter.NextLeak <> nil then
  8993. LPInsertAfter := LPInsertAfter.NextLeak
  8994. else
  8995. Break;
  8996. end;
  8997. if APNewEntry.LeakCount > 0 then
  8998. begin
  8999. {Get a position for the entry}
  9000. LPNewEntry := ExpectedMemoryLeaks.FirstFreeSlot;
  9001. if LPNewEntry <> nil then
  9002. begin
  9003. ExpectedMemoryLeaks.FirstFreeSlot := LPNewEntry.NextLeak;
  9004. end
  9005. else
  9006. begin
  9007. if ExpectedMemoryLeaks.EntriesUsed < Length(ExpectedMemoryLeaks.ExpectedLeaks) then
  9008. begin
  9009. LPNewEntry := @ExpectedMemoryLeaks.ExpectedLeaks[ExpectedMemoryLeaks.EntriesUsed];
  9010. Inc(ExpectedMemoryLeaks.EntriesUsed);
  9011. end
  9012. else
  9013. begin
  9014. {No more space}
  9015. Exit;
  9016. end;
  9017. end;
  9018. {Set the entry}
  9019. LPNewEntry^ := APNewEntry^;
  9020. {Insert it into the list}
  9021. LPNewEntry.PreviousLeak := LPInsertAfter;
  9022. if LPInsertAfter <> nil then
  9023. begin
  9024. LPNewEntry.NextLeak := LPInsertAfter.NextLeak;
  9025. if LPNewEntry.NextLeak <> nil then
  9026. LPNewEntry.NextLeak.PreviousLeak := LPNewEntry;
  9027. LPInsertAfter.NextLeak := LPNewEntry;
  9028. end
  9029. else
  9030. begin
  9031. LPNewEntry.NextLeak := APLeakList^;
  9032. if LPNewEntry.NextLeak <> nil then
  9033. LPNewEntry.NextLeak.PreviousLeak := LPNewEntry;
  9034. APLeakList^ := LPNewEntry;
  9035. end;
  9036. Result := True;
  9037. end;
  9038. end;
  9039. {Locks the expected leaks. Returns false if the list could not be allocated.}
  9040. function LockExpectedMemoryLeaksList: Boolean;
  9041. begin
  9042. {Lock the expected leaks list}
  9043. {$ifndef AssumeMultiThreaded}
  9044. if IsMultiThread then
  9045. {$endif}
  9046. begin
  9047. while LockCmpxchg(0, 1, @ExpectedMemoryLeaksListLocked) <> 0 do
  9048. begin
  9049. {$ifdef NeverSleepOnThreadContention}
  9050. {$ifdef UseSwitchToThread}
  9051. SwitchToThread;
  9052. {$endif}
  9053. {$else}
  9054. Sleep(InitialSleepTime);
  9055. if LockCmpxchg(0, 1, @ExpectedMemoryLeaksListLocked) = 0 then
  9056. Break;
  9057. Sleep(AdditionalSleepTime);
  9058. {$endif}
  9059. end;
  9060. end;
  9061. {Allocate the list if it does not exist}
  9062. if ExpectedMemoryLeaks = nil then
  9063. ExpectedMemoryLeaks := VirtualAlloc(nil, ExpectedMemoryLeaksListSize, MEM_COMMIT, PAGE_READWRITE);
  9064. {Done}
  9065. Result := ExpectedMemoryLeaks <> nil;
  9066. end;
  9067. {Registers expected memory leaks. Returns true on success. The list of leaked
  9068. blocks is limited, so failure is possible if the list is full.}
  9069. function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
  9070. var
  9071. LNewEntry: TExpectedMemoryLeak;
  9072. begin
  9073. {Fill out the structure}
  9074. {$ifndef FullDebugMode}
  9075. LNewEntry.LeakAddress := ALeakedPointer;
  9076. {$else}
  9077. LNewEntry.LeakAddress := Pointer(PByte(ALeakedPointer) - SizeOf(TFullDebugBlockHeader));
  9078. {$endif}
  9079. LNewEntry.LeakedClass := nil;
  9080. {$ifdef CheckCppObjectTypeEnabled}
  9081. LNewEntry.LeakedCppTypeIdPtr := nil;
  9082. {$endif}
  9083. LNewEntry.LeakSize := 0;
  9084. LNewEntry.LeakCount := 1;
  9085. {Add it to the correct list}
  9086. Result := LockExpectedMemoryLeaksList
  9087. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LNewEntry);
  9088. ExpectedMemoryLeaksListLocked := False;
  9089. end;
  9090. function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
  9091. var
  9092. LNewEntry: TExpectedMemoryLeak;
  9093. begin
  9094. {Fill out the structure}
  9095. LNewEntry.LeakAddress := nil;
  9096. LNewEntry.LeakedClass := ALeakedObjectClass;
  9097. {$ifdef CheckCppObjectTypeEnabled}
  9098. LNewEntry.LeakedCppTypeIdPtr := nil;
  9099. {$endif}
  9100. LNewEntry.LeakSize := ALeakedObjectClass.InstanceSize;
  9101. LNewEntry.LeakCount := ACount;
  9102. {Add it to the correct list}
  9103. Result := LockExpectedMemoryLeaksList
  9104. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LNewEntry);
  9105. ExpectedMemoryLeaksListLocked := False;
  9106. end;
  9107. {$ifdef CheckCppObjectTypeEnabled}
  9108. function RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): Boolean; overload;
  9109. var
  9110. LNewEntry: TExpectedMemoryLeak;
  9111. begin
  9112. {Fill out the structure}
  9113. if Assigned(GetCppVirtObjSizeByTypeIdPtrFunc) then
  9114. begin
  9115. //Return 0 if not a proper type
  9116. LNewEntry.LeakSize := GetCppVirtObjSizeByTypeIdPtrFunc(ALeakedCppVirtObjTypeIdPtr);
  9117. if LNewEntry.LeakSize > 0 then
  9118. begin
  9119. LNewEntry.LeakAddress := nil;
  9120. LNewEntry.LeakedClass := nil;
  9121. LNewEntry.LeakedCppTypeIdPtr := ALeakedCppVirtObjTypeIdPtr;
  9122. LNewEntry.LeakCount := ACount;
  9123. {Add it to the correct list}
  9124. Result := LockExpectedMemoryLeaksList
  9125. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LNewEntry);
  9126. ExpectedMemoryLeaksListLocked := False;
  9127. end
  9128. else
  9129. begin
  9130. Result := False;
  9131. end;
  9132. end
  9133. else
  9134. begin
  9135. Result := False;
  9136. end;
  9137. end;
  9138. {$endif}
  9139. function RegisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
  9140. var
  9141. LNewEntry: TExpectedMemoryLeak;
  9142. begin
  9143. {Fill out the structure}
  9144. LNewEntry.LeakAddress := nil;
  9145. LNewEntry.LeakedClass := nil;
  9146. {$ifdef CheckCppObjectTypeEnabled}
  9147. LNewEntry.LeakedCppTypeIdPtr := nil;
  9148. {$endif}
  9149. LNewEntry.LeakSize := ALeakedBlockSize;
  9150. LNewEntry.LeakCount := ACount;
  9151. {Add it to the correct list}
  9152. Result := LockExpectedMemoryLeaksList
  9153. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryBySizeOnly, @LNewEntry);
  9154. ExpectedMemoryLeaksListLocked := False;
  9155. end;
  9156. function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
  9157. var
  9158. LNewEntry: TExpectedMemoryLeak;
  9159. begin
  9160. {Fill out the structure}
  9161. {$ifndef FullDebugMode}
  9162. LNewEntry.LeakAddress := ALeakedPointer;
  9163. {$else}
  9164. LNewEntry.LeakAddress := Pointer(PByte(ALeakedPointer) - SizeOf(TFullDebugBlockHeader));
  9165. {$endif}
  9166. LNewEntry.LeakedClass := nil;
  9167. {$ifdef CheckCppObjectTypeEnabled}
  9168. LNewEntry.LeakedCppTypeIdPtr := nil;
  9169. {$endif}
  9170. LNewEntry.LeakSize := 0;
  9171. LNewEntry.LeakCount := -1;
  9172. {Remove it from the list}
  9173. Result := LockExpectedMemoryLeaksList
  9174. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LNewEntry);
  9175. ExpectedMemoryLeaksListLocked := False;
  9176. end;
  9177. function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
  9178. begin
  9179. Result := RegisterExpectedMemoryLeak(ALeakedObjectClass, - ACount);
  9180. end;
  9181. {$ifdef CheckCppObjectTypeEnabled}
  9182. function UnregisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): Boolean; overload;
  9183. begin
  9184. Result := RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr, - ACount);
  9185. end;
  9186. {$endif}
  9187. function UnregisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
  9188. begin
  9189. Result := RegisterExpectedMemoryLeak(ALeakedBlockSize, - ACount);
  9190. end;
  9191. {Returns a list of all expected memory leaks}
  9192. function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
  9193. procedure AddEntries(AEntry: PExpectedMemoryLeak);
  9194. var
  9195. LInd: Integer;
  9196. begin
  9197. while AEntry <> nil do
  9198. begin
  9199. LInd := Length(Result);
  9200. SetLength(Result, LInd + 1);
  9201. {Add the entry}
  9202. {$ifndef FullDebugMode}
  9203. Result[LInd].LeakAddress := AEntry.LeakAddress;
  9204. {$else}
  9205. Result[LInd].LeakAddress := Pointer(PByte(AEntry.LeakAddress) + SizeOf(TFullDebugBlockHeader));
  9206. {$endif}
  9207. Result[LInd].LeakedClass := AEntry.LeakedClass;
  9208. {$ifdef CheckCppObjectTypeEnabled}
  9209. Result[LInd].LeakedCppTypeIdPtr := AEntry.LeakedCppTypeIdPtr;
  9210. {$endif}
  9211. Result[LInd].LeakSize := AEntry.LeakSize;
  9212. Result[LInd].LeakCount := AEntry.LeakCount;
  9213. {Next entry}
  9214. AEntry := AEntry.NextLeak;
  9215. end;
  9216. end;
  9217. begin
  9218. SetLength(Result, 0);
  9219. if (ExpectedMemoryLeaks <> nil) and LockExpectedMemoryLeaksList then
  9220. begin
  9221. {Add all entries}
  9222. AddEntries(ExpectedMemoryLeaks.FirstEntryByAddress);
  9223. AddEntries(ExpectedMemoryLeaks.FirstEntryByClass);
  9224. AddEntries(ExpectedMemoryLeaks.FirstEntryBySizeOnly);
  9225. {Unlock the list}
  9226. ExpectedMemoryLeaksListLocked := False;
  9227. end;
  9228. end;
  9229. {$else}
  9230. {$ifdef BDS2006AndUp}
  9231. function NoOpRegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean;
  9232. begin
  9233. {Do nothing. Used when memory leak reporting is disabled under Delphi 2006 and later.}
  9234. Result := False;
  9235. end;
  9236. function NoOpUnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean;
  9237. begin
  9238. {Do nothing. Used when memory leak reporting is disabled under Delphi 2006 and later.}
  9239. Result := False;
  9240. end;
  9241. {$endif}
  9242. {$endif}
  9243. {Detects the probable string data type for a memory block.}
  9244. function DetectStringData(APMemoryBlock: Pointer;
  9245. AAvailableSpaceInBlock: NativeInt): TStringDataType;
  9246. const
  9247. {If the string reference count field contains a value greater than this,
  9248. then it is assumed that the block is not a string.}
  9249. MaxRefCount = 255;
  9250. {The lowest ASCII character code considered valid string data. If there are
  9251. any characters below this code point then the data is assumed not to be a
  9252. string. #9 = Tab.}
  9253. MinCharCode = #9;
  9254. var
  9255. LStringLength, LElemSize, LCharInd: Integer;
  9256. LPAnsiStr: PAnsiChar;
  9257. LPUniStr: PWideChar;
  9258. begin
  9259. {Check that the reference count is within a reasonable range}
  9260. if PStrRec(APMemoryBlock).refCnt > MaxRefCount then
  9261. begin
  9262. Result := stUnknown;
  9263. Exit;
  9264. end;
  9265. {$ifdef BCB6OrDelphi6AndUp}
  9266. {$if RTLVersion >= 20}
  9267. LElemSize := PStrRec(APMemoryBlock).elemSize;
  9268. {Element size must be either 1 (Ansi) or 2 (Unicode)}
  9269. if (LElemSize <> 1) and (LElemSize <> 2) then
  9270. begin
  9271. Result := stUnknown;
  9272. Exit;
  9273. end;
  9274. {$ifend}
  9275. {$if RTLVersion < 20}
  9276. LElemSize := 1;
  9277. {$ifend}
  9278. {$else}
  9279. LElemSize := 1;
  9280. {$endif}
  9281. {Get the string length}
  9282. LStringLength := PStrRec(APMemoryBlock).length;
  9283. {Does the string fit?}
  9284. if (LStringLength <= 0)
  9285. or (LStringLength >= (AAvailableSpaceInBlock - SizeOf(StrRec)) div LElemSize) then
  9286. begin
  9287. Result := stUnknown;
  9288. Exit;
  9289. end;
  9290. {Check for no characters outside the expected range. If there are,
  9291. then it is probably not a string.}
  9292. if LElemSize = 1 then
  9293. begin
  9294. {Check that all characters are in the range considered valid.}
  9295. LPAnsiStr := PAnsiChar(PByte(APMemoryBlock) + SizeOf(StrRec));
  9296. for LCharInd := 1 to LStringLength do
  9297. begin
  9298. if LPAnsiStr^ < MinCharCode then
  9299. begin
  9300. Result := stUnknown;
  9301. Exit;
  9302. end;
  9303. Inc(LPAnsiStr);
  9304. end;
  9305. {Must have a trailing #0}
  9306. if LPAnsiStr^ = #0 then
  9307. Result := stAnsiString
  9308. else
  9309. Result := stUnknown;
  9310. end
  9311. else
  9312. begin
  9313. {Check that all characters are in the range considered valid.}
  9314. LPUniStr := PWideChar(PByte(APMemoryBlock) + SizeOf(StrRec));
  9315. for LCharInd := 1 to LStringLength do
  9316. begin
  9317. if LPUniStr^ < MinCharCode then
  9318. begin
  9319. Result := stUnknown;
  9320. Exit;
  9321. end;
  9322. Inc(LPUniStr);
  9323. end;
  9324. {Must have a trailing #0}
  9325. if LPUniStr^ = #0 then
  9326. Result := stUnicodeString
  9327. else
  9328. Result := stUnknown;
  9329. end;
  9330. end;
  9331. {Checks blocks for modification after free and also for memory leaks}
  9332. procedure CheckBlocksOnShutdown(ACheckForLeakedBlocks: Boolean);
  9333. {$ifdef EnableMemoryLeakReporting}
  9334. type
  9335. {Leaked class type}
  9336. TLeakedClass = packed record
  9337. ClassPointer: TClass;
  9338. {$ifdef CheckCppObjectTypeEnabled}
  9339. CppTypeIdPtr: Pointer;
  9340. {$endif}
  9341. NumLeaks: Cardinal;
  9342. end;
  9343. TLeakedClasses = array[0..255] of TLeakedClass;
  9344. PLeakedClasses = ^TLeakedClasses;
  9345. {Leak statistics for a small block type}
  9346. TSmallBlockLeaks = array[0..NumSmallBlockTypes - 1] of TLeakedClasses;
  9347. {A leaked medium or large block}
  9348. TMediumAndLargeBlockLeaks = array[0..4095] of NativeUInt;
  9349. {$endif}
  9350. var
  9351. {$ifdef EnableMemoryLeakReporting}
  9352. {The leaked classes for small blocks}
  9353. LSmallBlockLeaks: TSmallBlockLeaks;
  9354. LLeakType: TMemoryLeakType;
  9355. {$ifdef CheckCppObjectTypeEnabled}
  9356. LLeakedCppTypeIdPtr: Pointer;
  9357. LCppTypeName: PAnsiChar;
  9358. {$endif}
  9359. LMediumAndLargeBlockLeaks: TMediumAndLargeBlockLeaks;
  9360. LNumMediumAndLargeLeaks: Integer;
  9361. LPLargeBlock: PLargeBlockHeader;
  9362. LLeakMessage: array[0..32767] of AnsiChar;
  9363. {$ifndef NoMessageBoxes}
  9364. LMessageTitleBuffer: array[0..1023] of AnsiChar;
  9365. {$endif}
  9366. LMsgPtr: PAnsiChar;
  9367. LExpectedLeaksOnly, LSmallLeakHeaderAdded, LBlockSizeHeaderAdded: Boolean;
  9368. LBlockTypeInd, LClassInd, LBlockInd: Cardinal;
  9369. LMediumBlockSize, LPreviousBlockSize, LLargeBlockSize, LThisBlockSize: NativeUInt;
  9370. {$endif}
  9371. LPMediumBlock: Pointer;
  9372. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  9373. LMediumBlockHeader: NativeUInt;
  9374. {$ifdef EnableMemoryLeakReporting}
  9375. {Tries to account for a memory leak. Returns true if the leak is expected and
  9376. removes the leak from the list}
  9377. function GetMemoryLeakType(AAddress: Pointer; ASpaceInsideBlock: NativeUInt): TMemoryLeakType;
  9378. var
  9379. LLeak: TExpectedMemoryLeak;
  9380. begin
  9381. {Default to not found}
  9382. Result := mltUnexpectedLeak;
  9383. if ExpectedMemoryLeaks <> nil then
  9384. begin
  9385. {Check by pointer address}
  9386. LLeak.LeakAddress := AAddress;
  9387. LLeak.LeakedClass := nil;
  9388. {$ifdef CheckCppObjectTypeEnabled}
  9389. LLeak.LeakedCppTypeIdPtr := nil;
  9390. {$endif}
  9391. LLeak.LeakSize := 0;
  9392. LLeak.LeakCount := -1;
  9393. if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LLeak, False) then
  9394. begin
  9395. Result := mltExpectedLeakRegisteredByPointer;
  9396. Exit;
  9397. end;
  9398. {Check by class}
  9399. LLeak.LeakAddress := nil;
  9400. {$ifdef FullDebugMode}
  9401. LLeak.LeakedClass := TClass(PNativeUInt(PByte(AAddress)+ SizeOf(TFullDebugBlockHeader))^);
  9402. {$else}
  9403. LLeak.LeakedClass := TClass(PNativeUInt(AAddress)^);
  9404. {$endif}
  9405. {$ifdef CheckCppObjectTypeEnabled}
  9406. if Assigned(GetCppVirtObjTypeIdPtrFunc) then
  9407. begin
  9408. {$ifdef FullDebugMode}
  9409. LLeak.LeakedCppTypeIdPtr := GetCppVirtObjTypeIdPtrFunc(Pointer(PByte(AAddress)
  9410. + SizeOf(TFullDebugBlockHeader)), ASpaceInsideBlock);
  9411. {$else}
  9412. LLeak.LeakedCppTypeIdPtr := GetCppVirtObjTypeIdPtrFunc(AAddress, ASpaceInsideBlock);
  9413. {$endif}
  9414. end;
  9415. LLeakedCppTypeIdPtr := LLeak.LeakedCppTypeIdPtr;
  9416. {$endif}
  9417. LLeak.LeakSize := ASpaceInsideBlock;
  9418. if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LLeak, False) then
  9419. begin
  9420. Result := mltExpectedLeakRegisteredByClass;
  9421. Exit;
  9422. end;
  9423. {Check by size: the block must be large enough to hold the leak}
  9424. LLeak.LeakedClass := nil;
  9425. if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryBySizeOnly, @LLeak, False) then
  9426. Result := mltExpectedLeakRegisteredBySize;
  9427. end;
  9428. end;
  9429. {Checks the small block pool for leaks.}
  9430. procedure CheckSmallBlockPoolForLeaks(APSmallBlockPool: PSmallBlockPoolHeader);
  9431. var
  9432. LLeakedClass: TClass;
  9433. {$ifdef CheckCppObjectTypeEnabled}
  9434. LLeakedCppObjectTypeId: Pointer;
  9435. {$endif}
  9436. LSmallBlockLeakType: TMemoryLeakType;
  9437. LClassIndex: Integer;
  9438. LCurPtr, LEndPtr, LDataPtr: Pointer;
  9439. LBlockTypeIndex: Cardinal;
  9440. LPLeakedClasses: PLeakedClasses;
  9441. LSmallBlockSize: Cardinal;
  9442. begin
  9443. {Get the useable size inside a block}
  9444. LSmallBlockSize := APSmallBlockPool.BlockType.BlockSize - BlockHeaderSize;
  9445. {$ifdef FullDebugMode}
  9446. Dec(LSmallBlockSize, FullDebugBlockOverhead);
  9447. {$endif}
  9448. {Get the block type index}
  9449. LBlockTypeIndex := (UIntPtr(APSmallBlockPool.BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
  9450. LPLeakedClasses := @LSmallBlockLeaks[LBlockTypeIndex];
  9451. {Get the first and last pointer for the pool}
  9452. GetFirstAndLastSmallBlockInPool(APSmallBlockPool, LCurPtr, LEndPtr);
  9453. {Step through all blocks}
  9454. while UIntPtr(LCurPtr) <= UIntPtr(LEndPtr) do
  9455. begin
  9456. {Is this block in use? If so, is the debug info intact?}
  9457. if ((PNativeUInt(PByte(LCurPtr) - BlockHeaderSize)^ and IsFreeBlockFlag) = 0) then
  9458. begin
  9459. {$ifdef FullDebugMode}
  9460. if CheckBlockBeforeFreeOrRealloc(LCurPtr, boBlockCheck) then
  9461. {$endif}
  9462. begin
  9463. {$ifdef CheckCppObjectTypeEnabled}
  9464. LLeakedCppTypeIdPtr := nil;
  9465. {$endif}
  9466. {Get the leak type}
  9467. LSmallBlockLeakType := GetMemoryLeakType(LCurPtr, LSmallBlockSize);
  9468. {$ifdef LogMemoryLeakDetailToFile}
  9469. {$ifdef HideExpectedLeaksRegisteredByPointer}
  9470. if LSmallBlockLeakType <> mltExpectedLeakRegisteredByPointer then
  9471. {$endif}
  9472. LogMemoryLeakOrAllocatedBlock(LCurPtr, True);
  9473. {$endif}
  9474. {Only expected leaks?}
  9475. LExpectedLeaksOnly := LExpectedLeaksOnly and (LSmallBlockLeakType <> mltUnexpectedLeak);
  9476. {$ifdef HideExpectedLeaksRegisteredByPointer}
  9477. if LSmallBlockLeakType <> mltExpectedLeakRegisteredByPointer then
  9478. {$endif}
  9479. begin
  9480. {Get a pointer to the user data}
  9481. {$ifndef FullDebugMode}
  9482. LDataPtr := LCurPtr;
  9483. {$else}
  9484. LDataPtr := Pointer(PByte(LCurPtr) + SizeOf(TFullDebugBlockHeader));
  9485. {$endif}
  9486. {Default to an unknown block}
  9487. LClassIndex := 0;
  9488. {Get the class contained by the block}
  9489. LLeakedClass := DetectClassInstance(LDataPtr);
  9490. {Not a Delphi class? -> is it perhaps a string or C++ object type?}
  9491. if LLeakedClass = nil then
  9492. begin
  9493. {$ifdef CheckCppObjectTypeEnabled}
  9494. LLeakedCppObjectTypeId := LLeakedCppTypeIdPtr;
  9495. if (LLeakedCppObjectTypeId = nil) and (ExpectedMemoryLeaks = nil) then
  9496. begin
  9497. if Assigned(GetCppVirtObjTypeIdPtrFunc) then
  9498. begin
  9499. LLeakedCppObjectTypeId := GetCppVirtObjTypeIdPtrFunc(LDataPtr, LSmallBlockSize);
  9500. end;
  9501. end;
  9502. if Assigned(LLeakedCppObjectTypeId) then
  9503. begin
  9504. LClassIndex := 3;
  9505. while LClassIndex <= High(TLeakedClasses) do
  9506. begin
  9507. if (Pointer(LPLeakedClasses[LClassIndex].CppTypeIdPtr) = LLeakedCppObjectTypeId)
  9508. or ((LPLeakedClasses[LClassIndex].CppTypeIdPtr = nil)
  9509. and (LPLeakedClasses[LClassIndex].ClassPointer = nil)) then
  9510. begin
  9511. Break;
  9512. end;
  9513. Inc(LClassIndex);
  9514. end;
  9515. if LClassIndex <= High(TLeakedClasses) then
  9516. Pointer(LPLeakedClasses[LClassIndex].CppTypeIdPtr) := LLeakedCppObjectTypeId
  9517. else
  9518. LClassIndex := 0;
  9519. end
  9520. else
  9521. begin
  9522. {$endif}
  9523. {Not a known class: Is it perhaps string data?}
  9524. case DetectStringData(LDataPtr, APSmallBlockPool.BlockType.BlockSize - (BlockHeaderSize {$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif})) of
  9525. stAnsiString: LClassIndex := 1;
  9526. stUnicodeString: LClassIndex := 2;
  9527. end;
  9528. {$ifdef CheckCppObjectTypeEnabled}
  9529. end;
  9530. {$endif}
  9531. end
  9532. else
  9533. begin
  9534. LClassIndex := 3;
  9535. while LClassIndex <= High(TLeakedClasses) do
  9536. begin
  9537. if (LPLeakedClasses[LClassIndex].ClassPointer = LLeakedClass)
  9538. or ((LPLeakedClasses[LClassIndex].ClassPointer = nil)
  9539. {$ifdef CheckCppObjectTypeEnabled}
  9540. and (LPLeakedClasses[LClassIndex].CppTypeIdPtr = nil)
  9541. {$endif}
  9542. ) then
  9543. begin
  9544. Break;
  9545. end;
  9546. Inc(LClassIndex);
  9547. end;
  9548. if LClassIndex <= High(TLeakedClasses) then
  9549. LPLeakedClasses[LClassIndex].ClassPointer := LLeakedClass
  9550. else
  9551. LClassIndex := 0;
  9552. end;
  9553. {Add to the number of leaks for the class}
  9554. Inc(LPLeakedClasses[LClassIndex].NumLeaks);
  9555. end;
  9556. end;
  9557. end
  9558. else
  9559. begin
  9560. {$ifdef CheckUseOfFreedBlocksOnShutdown}
  9561. {Check that the block has not been modified since being freed}
  9562. CheckFreeBlockUnmodified(LCurPtr, APSmallBlockPool.BlockType.BlockSize, boBlockCheck);
  9563. {$endif}
  9564. end;
  9565. {Next block}
  9566. Inc(PByte(LCurPtr), APSmallBlockPool.BlockType.BlockSize);
  9567. end;
  9568. end;
  9569. {$endif}
  9570. begin
  9571. {$ifdef EnableMemoryLeakReporting}
  9572. {Clear the leak arrays}
  9573. FillChar(LSmallBlockLeaks, SizeOf(LSmallBlockLeaks), 0);
  9574. FillChar(LMediumAndLargeBlockLeaks, SizeOf(LMediumAndLargeBlockLeaks), 0);
  9575. {Step through all the medium block pools}
  9576. LNumMediumAndLargeLeaks := 0;
  9577. {No unexpected leaks so far}
  9578. LExpectedLeaksOnly := True;
  9579. {$endif}
  9580. {Step through all the medium block pools}
  9581. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  9582. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  9583. begin
  9584. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  9585. while LPMediumBlock <> nil do
  9586. begin
  9587. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  9588. {Is the block in use?}
  9589. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  9590. begin
  9591. {$ifdef EnableMemoryLeakReporting}
  9592. if ACheckForLeakedBlocks then
  9593. begin
  9594. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  9595. begin
  9596. {Get all the leaks for the small block pool}
  9597. CheckSmallBlockPoolForLeaks(LPMediumBlock);
  9598. end
  9599. else
  9600. begin
  9601. if (LNumMediumAndLargeLeaks < Length(LMediumAndLargeBlockLeaks))
  9602. {$ifdef FullDebugMode}
  9603. and CheckBlockBeforeFreeOrRealloc(LPMediumBlock, boBlockCheck)
  9604. {$endif}
  9605. then
  9606. begin
  9607. LMediumBlockSize := (LMediumBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
  9608. {$ifdef FullDebugMode}
  9609. Dec(LMediumBlockSize, FullDebugBlockOverhead);
  9610. {$endif}
  9611. {Get the leak type}
  9612. LLeakType := GetMemoryLeakType(LPMediumBlock, LMediumBlockSize);
  9613. {Is it an expected leak?}
  9614. LExpectedLeaksOnly := LExpectedLeaksOnly and (LLeakType <> mltUnexpectedLeak);
  9615. {$ifdef LogMemoryLeakDetailToFile}
  9616. {$ifdef HideExpectedLeaksRegisteredByPointer}
  9617. if LLeakType <> mltExpectedLeakRegisteredByPointer then
  9618. {$endif}
  9619. LogMemoryLeakOrAllocatedBlock(LPMediumBlock, True);
  9620. {$endif}
  9621. {$ifdef HideExpectedLeaksRegisteredByPointer}
  9622. if LLeakType <> mltExpectedLeakRegisteredByPointer then
  9623. {$endif}
  9624. begin
  9625. {Add the leak to the list}
  9626. LMediumAndLargeBlockLeaks[LNumMediumAndLargeLeaks] := LMediumBlockSize;
  9627. Inc(LNumMediumAndLargeLeaks);
  9628. end;
  9629. end;
  9630. end;
  9631. end;
  9632. {$endif}
  9633. end
  9634. else
  9635. begin
  9636. {$ifdef CheckUseOfFreedBlocksOnShutdown}
  9637. {Check that the block has not been modified since being freed}
  9638. CheckFreeBlockUnmodified(LPMediumBlock, LMediumBlockHeader and DropMediumAndLargeFlagsMask, boBlockCheck);
  9639. {$endif}
  9640. end;
  9641. {Next medium block}
  9642. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  9643. end;
  9644. {Get the next medium block pool}
  9645. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  9646. end;
  9647. {$ifdef EnableMemoryLeakReporting}
  9648. if ACheckForLeakedBlocks then
  9649. begin
  9650. {Get all leaked large blocks}
  9651. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  9652. while LPLargeBlock <> @LargeBlocksCircularList do
  9653. begin
  9654. if (LNumMediumAndLargeLeaks < length(LMediumAndLargeBlockLeaks))
  9655. {$ifdef FullDebugMode}
  9656. and CheckBlockBeforeFreeOrRealloc(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), boBlockCheck)
  9657. {$endif}
  9658. then
  9659. begin
  9660. LLargeBlockSize := (LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask) - BlockHeaderSize - LargeBlockHeaderSize;
  9661. {$ifdef FullDebugMode}
  9662. Dec(LLargeBlockSize, FullDebugBlockOverhead);
  9663. {$endif}
  9664. {Get the leak type}
  9665. LLeakType := GetMemoryLeakType(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), LLargeBlockSize);
  9666. {Is it an expected leak?}
  9667. LExpectedLeaksOnly := LExpectedLeaksOnly and (LLeakType <> mltUnexpectedLeak);
  9668. {$ifdef LogMemoryLeakDetailToFile}
  9669. {$ifdef HideExpectedLeaksRegisteredByPointer}
  9670. if LLeakType <> mltExpectedLeakRegisteredByPointer then
  9671. {$endif}
  9672. LogMemoryLeakOrAllocatedBlock(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), True);
  9673. {$endif}
  9674. {$ifdef HideExpectedLeaksRegisteredByPointer}
  9675. if LLeakType <> mltExpectedLeakRegisteredByPointer then
  9676. {$endif}
  9677. begin
  9678. {Add the leak}
  9679. LMediumAndLargeBlockLeaks[LNumMediumAndLargeLeaks] := LLargeBlockSize;
  9680. Inc(LNumMediumAndLargeLeaks);
  9681. end;
  9682. end;
  9683. {Get the next large block}
  9684. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  9685. end;
  9686. {Display the leak message if required}
  9687. if not LExpectedLeaksOnly then
  9688. begin
  9689. {Small leak header has not been added}
  9690. LSmallLeakHeaderAdded := False;
  9691. LPreviousBlockSize := 0;
  9692. {Set up the leak message header so long}
  9693. LMsgPtr := AppendStringToBuffer(LeakMessageHeader, @LLeakMessage[0], length(LeakMessageHeader));
  9694. {Step through all the small block types}
  9695. for LBlockTypeInd := 0 to NumSmallBlockTypes - 1 do
  9696. begin
  9697. LThisBlockSize := SmallBlockTypes[LBlockTypeInd].BlockSize - BlockHeaderSize;
  9698. {$ifdef FullDebugMode}
  9699. Dec(LThisBlockSize, FullDebugBlockOverhead);
  9700. if NativeInt(LThisBlockSize) < 0 then
  9701. LThisBlockSize := 0;
  9702. {$endif}
  9703. LBlockSizeHeaderAdded := False;
  9704. {Any leaks?}
  9705. for LClassInd := High(LSmallBlockLeaks[LBlockTypeInd]) downto 0 do
  9706. begin
  9707. {Is there still space in the message buffer? Reserve space for the message
  9708. footer.}
  9709. if LMsgPtr > @LLeakMessage[High(LLeakMessage) - 2048] then
  9710. Break;
  9711. {Check the count}
  9712. if LSmallBlockLeaks[LBlockTypeInd][LClassInd].NumLeaks > 0 then
  9713. begin
  9714. {Need to add the header?}
  9715. if not LSmallLeakHeaderAdded then
  9716. begin
  9717. LMsgPtr := AppendStringToBuffer(SmallLeakDetail, LMsgPtr, Length(SmallLeakDetail));
  9718. LSmallLeakHeaderAdded := True;
  9719. end;
  9720. {Need to add the size header?}
  9721. if not LBlockSizeHeaderAdded then
  9722. begin
  9723. LMsgPtr^ := #13;
  9724. Inc(LMsgPtr);
  9725. LMsgPtr^ := #10;
  9726. Inc(LMsgPtr);
  9727. LMsgPtr := NativeUIntToStrBuf(LPreviousBlockSize + 1, LMsgPtr);
  9728. LMsgPtr^ := ' ';
  9729. Inc(LMsgPtr);
  9730. LMsgPtr^ := '-';
  9731. Inc(LMsgPtr);
  9732. LMsgPtr^ := ' ';
  9733. Inc(LMsgPtr);
  9734. LMsgPtr := NativeUIntToStrBuf(LThisBlockSize, LMsgPtr);
  9735. LMsgPtr := AppendStringToBuffer(BytesMessage, LMsgPtr, Length(BytesMessage));
  9736. LBlockSizeHeaderAdded := True;
  9737. end
  9738. else
  9739. begin
  9740. LMsgPtr^ := ',';
  9741. Inc(LMsgPtr);
  9742. LMsgPtr^ := ' ';
  9743. Inc(LMsgPtr);
  9744. end;
  9745. {Show the count}
  9746. case LClassInd of
  9747. {Unknown}
  9748. 0:
  9749. begin
  9750. LMsgPtr := AppendStringToBuffer(UnknownClassNameMsg, LMsgPtr, Length(UnknownClassNameMsg));
  9751. end;
  9752. {AnsiString}
  9753. 1:
  9754. begin
  9755. LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
  9756. end;
  9757. {UnicodeString}
  9758. 2:
  9759. begin
  9760. LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
  9761. end;
  9762. {Classes}
  9763. else
  9764. begin
  9765. {$ifdef CheckCppObjectTypeEnabled}
  9766. if LSmallBlockLeaks[LBlockTypeInd][LClassInd].CppTypeIdPtr <> nil then
  9767. begin
  9768. if Assigned(GetCppVirtObjTypeNameByTypeIdPtrFunc) then
  9769. begin
  9770. LCppTypeName := GetCppVirtObjTypeNameByTypeIdPtrFunc(LSmallBlockLeaks[LBlockTypeInd][LClassInd].CppTypeIdPtr);
  9771. LMsgPtr := AppendStringToBuffer(LCppTypeName, LMsgPtr, StrLen(LCppTypeName));
  9772. end
  9773. else
  9774. LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
  9775. end
  9776. else
  9777. begin
  9778. {$endif}
  9779. LMsgPtr := AppendClassNameToBuffer(LSmallBlockLeaks[LBlockTypeInd][LClassInd].ClassPointer, LMsgPtr);
  9780. {$ifdef CheckCppObjectTypeEnabled}
  9781. end;
  9782. {$endif}
  9783. end;
  9784. end;
  9785. {Add the count}
  9786. LMsgPtr^ := ' ';
  9787. Inc(LMsgPtr);
  9788. LMsgPtr^ := 'x';
  9789. Inc(LMsgPtr);
  9790. LMsgPtr^ := ' ';
  9791. Inc(LMsgPtr);
  9792. LMsgPtr := NativeUIntToStrBuf(LSmallBlockLeaks[LBlockTypeInd][LClassInd].NumLeaks, LMsgPtr);
  9793. end;
  9794. end;
  9795. LPreviousBlockSize := LThisBlockSize;
  9796. end;
  9797. {Add the medium/large block leak message}
  9798. if LNumMediumAndLargeLeaks > 0 then
  9799. begin
  9800. {Any non-small leaks?}
  9801. if LSmallLeakHeaderAdded then
  9802. begin
  9803. LMsgPtr^ := #13;
  9804. Inc(LMsgPtr);
  9805. LMsgPtr^ := #10;
  9806. Inc(LMsgPtr);
  9807. LMsgPtr^ := #13;
  9808. Inc(LMsgPtr);
  9809. LMsgPtr^ := #10;
  9810. Inc(LMsgPtr);
  9811. end;
  9812. {Add the medium/large block leak message}
  9813. LMsgPtr := AppendStringToBuffer(LargeLeakDetail, LMsgPtr, Length(LargeLeakDetail));
  9814. {List all the blocks}
  9815. for LBlockInd := 0 to LNumMediumAndLargeLeaks - 1 do
  9816. begin
  9817. if LBlockInd <> 0 then
  9818. begin
  9819. LMsgPtr^ := ',';
  9820. Inc(LMsgPtr);
  9821. LMsgPtr^ := ' ';
  9822. Inc(LMsgPtr);
  9823. end;
  9824. LMsgPtr := NativeUIntToStrBuf(LMediumAndLargeBlockLeaks[LBlockInd], LMsgPtr);
  9825. {Is there still space in the message buffer? Reserve space for the
  9826. message footer.}
  9827. if LMsgPtr > @LLeakMessage[High(LLeakMessage) - 2048] then
  9828. Break;
  9829. end;
  9830. end;
  9831. {$ifdef LogErrorsToFile}
  9832. {Set the message footer}
  9833. LMsgPtr := AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
  9834. {Append the message to the memory errors file}
  9835. AppendEventLog(@LLeakMessage[0], UIntPtr(LMsgPtr) - UIntPtr(@LLeakMessage[1]));
  9836. {$else}
  9837. {Set the message footer}
  9838. AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
  9839. {$endif}
  9840. {$ifdef UseOutputDebugString}
  9841. OutputDebugStringA(LLeakMessage);
  9842. {$endif}
  9843. {$ifndef NoMessageBoxes}
  9844. {Show the message}
  9845. AppendStringToModuleName(LeakMessageTitle, LMessageTitleBuffer);
  9846. ShowMessageBox(LLeakMessage, LMessageTitleBuffer);
  9847. {$endif}
  9848. end;
  9849. end;
  9850. {$endif}
  9851. end;
  9852. {Returns statistics about the current state of the memory manager}
  9853. procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
  9854. var
  9855. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  9856. LPMediumBlock: Pointer;
  9857. LInd: Integer;
  9858. LBlockTypeIndex, LMediumBlockSize: Cardinal;
  9859. LMediumBlockHeader, LLargeBlockSize: NativeUInt;
  9860. LPLargeBlock: PLargeBlockHeader;
  9861. begin
  9862. {Clear the structure}
  9863. FillChar(AMemoryManagerState, SizeOf(AMemoryManagerState), 0);
  9864. {Set the small block size stats}
  9865. for LInd := 0 to NumSmallBlockTypes - 1 do
  9866. begin
  9867. AMemoryManagerState.SmallBlockTypeStates[LInd].InternalBlockSize :=
  9868. SmallBlockTypes[LInd].BlockSize;
  9869. AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize :=
  9870. SmallBlockTypes[LInd].BlockSize - BlockHeaderSize{$ifdef FullDebugMode} - FullDebugBlockOverhead{$endif};
  9871. if NativeInt(AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize) < 0 then
  9872. AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize := 0;
  9873. end;
  9874. {Lock all small block types}
  9875. LockAllSmallBlockTypes;
  9876. {Lock the medium blocks}
  9877. LockMediumBlocks;
  9878. {Step through all the medium block pools}
  9879. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  9880. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  9881. begin
  9882. {Add to the medium block used space}
  9883. Inc(AMemoryManagerState.ReservedMediumBlockAddressSpace, MediumBlockPoolSize);
  9884. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  9885. while LPMediumBlock <> nil do
  9886. begin
  9887. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  9888. {Is the block in use?}
  9889. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  9890. begin
  9891. {Get the block size}
  9892. LMediumBlockSize := LMediumBlockHeader and DropMediumAndLargeFlagsMask;
  9893. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  9894. begin
  9895. {Get the block type index}
  9896. LBlockTypeIndex := (UIntPtr(PSmallBlockPoolHeader(LPMediumBlock).BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
  9897. {Subtract from medium block usage}
  9898. Dec(AMemoryManagerState.ReservedMediumBlockAddressSpace, LMediumBlockSize);
  9899. {Add it to the reserved space for the block size}
  9900. Inc(AMemoryManagerState.SmallBlockTypeStates[LBlockTypeIndex].ReservedAddressSpace, LMediumBlockSize);
  9901. {Add the usage for the pool}
  9902. Inc(AMemoryManagerState.SmallBlockTypeStates[LBlockTypeIndex].AllocatedBlockCount,
  9903. PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse);
  9904. end
  9905. else
  9906. begin
  9907. {$ifdef FullDebugMode}
  9908. Dec(LMediumBlockSize, FullDebugBlockOverhead);
  9909. {$endif}
  9910. Inc(AMemoryManagerState.AllocatedMediumBlockCount);
  9911. Inc(AMemoryManagerState.TotalAllocatedMediumBlockSize, LMediumBlockSize - BlockHeaderSize);
  9912. end;
  9913. end;
  9914. {Next medium block}
  9915. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  9916. end;
  9917. {Get the next medium block pool}
  9918. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  9919. end;
  9920. {Unlock medium blocks}
  9921. MediumBlocksLocked := False;
  9922. {Unlock all the small block types}
  9923. for LInd := 0 to NumSmallBlockTypes - 1 do
  9924. SmallBlockTypes[LInd].BlockTypeLocked := False;
  9925. {Step through all the large blocks}
  9926. LockLargeBlocks;
  9927. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  9928. while LPLargeBlock <> @LargeBlocksCircularList do
  9929. begin
  9930. LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  9931. Inc(AMemoryManagerState.AllocatedLargeBlockCount);
  9932. Inc(AMemoryManagerState.ReservedLargeBlockAddressSpace, LLargeBlockSize);
  9933. Inc(AMemoryManagerState.TotalAllocatedLargeBlockSize, LPLargeBlock.UserAllocatedSize);
  9934. {Get the next large block}
  9935. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  9936. end;
  9937. LargeBlocksLocked := False;
  9938. end;
  9939. {Returns a summary of the information returned by GetMemoryManagerState}
  9940. procedure GetMemoryManagerUsageSummary(
  9941. var AMemoryManagerUsageSummary: TMemoryManagerUsageSummary);
  9942. var
  9943. LMMS: TMemoryManagerState;
  9944. LAllocatedBytes, LReservedBytes: NativeUInt;
  9945. LSBTIndex: Integer;
  9946. begin
  9947. {Get the memory manager state}
  9948. GetMemoryManagerState(LMMS);
  9949. {Add up the totals}
  9950. LAllocatedBytes := LMMS.TotalAllocatedMediumBlockSize
  9951. + LMMS.TotalAllocatedLargeBlockSize;
  9952. LReservedBytes := LMMS.ReservedMediumBlockAddressSpace
  9953. + LMMS.ReservedLargeBlockAddressSpace;
  9954. for LSBTIndex := 0 to NumSmallBlockTypes - 1 do
  9955. begin
  9956. Inc(LAllocatedBytes, LMMS.SmallBlockTypeStates[LSBTIndex].UseableBlockSize
  9957. * LMMS.SmallBlockTypeStates[LSBTIndex].AllocatedBlockCount);
  9958. Inc(LReservedBytes, LMMS.SmallBlockTypeStates[LSBTIndex].ReservedAddressSpace);
  9959. end;
  9960. {Set the structure values}
  9961. AMemoryManagerUsageSummary.AllocatedBytes := LAllocatedBytes;
  9962. AMemoryManagerUsageSummary.OverheadBytes := LReservedBytes - LAllocatedBytes;
  9963. if LReservedBytes > 0 then
  9964. begin
  9965. AMemoryManagerUsageSummary.EfficiencyPercentage :=
  9966. LAllocatedBytes / LReservedBytes * 100;
  9967. end
  9968. else
  9969. AMemoryManagerUsageSummary.EfficiencyPercentage := 100;
  9970. end;
  9971. {$ifndef Linux}
  9972. {Gets the state of every 64K block in the 4GB address space. Under 64-bit this
  9973. returns only the state for the low 4GB.}
  9974. procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
  9975. var
  9976. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  9977. LPLargeBlock: PLargeBlockHeader;
  9978. LInd, LChunkIndex, LNextChunk, LLargeBlockSize: NativeUInt;
  9979. LMBI: TMemoryBasicInformation;
  9980. begin
  9981. {Clear the map}
  9982. FillChar(AMemoryMap, SizeOf(AMemoryMap), Ord(csUnallocated));
  9983. {Step through all the medium block pools}
  9984. LockMediumBlocks;
  9985. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  9986. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  9987. begin
  9988. {Add to the medium block used space}
  9989. LChunkIndex := NativeUInt(LPMediumBlockPoolHeader) shr 16;
  9990. for LInd := 0 to (MediumBlockPoolSize - 1) shr 16 do
  9991. begin
  9992. if (LChunkIndex + LInd) > High(AMemoryMap) then
  9993. Break;
  9994. AMemoryMap[LChunkIndex + LInd] := csAllocated;
  9995. end;
  9996. {Get the next medium block pool}
  9997. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  9998. end;
  9999. MediumBlocksLocked := False;
  10000. {Step through all the large blocks}
  10001. LockLargeBlocks;
  10002. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  10003. while LPLargeBlock <> @LargeBlocksCircularList do
  10004. begin
  10005. LChunkIndex := UIntPtr(LPLargeBlock) shr 16;
  10006. LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  10007. for LInd := 0 to (LLargeBlockSize - 1) shr 16 do
  10008. begin
  10009. if (LChunkIndex + LInd) > High(AMemoryMap) then
  10010. Break;
  10011. AMemoryMap[LChunkIndex + LInd] := csAllocated;
  10012. end;
  10013. {Get the next large block}
  10014. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  10015. end;
  10016. LargeBlocksLocked := False;
  10017. {Fill in the rest of the map}
  10018. LInd := 0;
  10019. while LInd <= 65535 do
  10020. begin
  10021. {If the chunk is not allocated by this MM, what is its status?}
  10022. if AMemoryMap[LInd] = csUnallocated then
  10023. begin
  10024. {Query the address space starting at the chunk boundary}
  10025. if VirtualQuery(Pointer(LInd * 65536), LMBI, SizeOf(LMBI)) = 0 then
  10026. begin
  10027. {VirtualQuery may fail for addresses >2GB if a large address space is
  10028. not enabled.}
  10029. FillChar(AMemoryMap[LInd], 65536 - LInd, csSysReserved);
  10030. Break;
  10031. end;
  10032. {Get the chunk number after the region}
  10033. LNextChunk := (LMBI.RegionSize - 1) shr 16 + LInd + 1;
  10034. {Validate}
  10035. if LNextChunk > 65536 then
  10036. LNextChunk := 65536;
  10037. {Set the status of all the chunks in the region}
  10038. if LMBI.State = MEM_COMMIT then
  10039. begin
  10040. FillChar(AMemoryMap[LInd], LNextChunk - LInd, csSysAllocated);
  10041. end
  10042. else
  10043. begin
  10044. if LMBI.State = MEM_RESERVE then
  10045. FillChar(AMemoryMap[LInd], LNextChunk - LInd, csSysReserved);
  10046. end;
  10047. {Point to the start of the next chunk}
  10048. LInd := LNextChunk;
  10049. end
  10050. else
  10051. begin
  10052. {Next chunk}
  10053. Inc(LInd);
  10054. end;
  10055. end;
  10056. end;
  10057. {$endif}
  10058. {Returns summarised information about the state of the memory manager. (For
  10059. backward compatibility.)}
  10060. function FastGetHeapStatus: THeapStatus;
  10061. var
  10062. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  10063. LPMediumBlock: Pointer;
  10064. LBlockTypeIndex, LMediumBlockSize: Cardinal;
  10065. LSmallBlockUsage, LSmallBlockOverhead, LMediumBlockHeader, LLargeBlockSize: NativeUInt;
  10066. LInd: Integer;
  10067. LPLargeBlock: PLargeBlockHeader;
  10068. begin
  10069. {Clear the structure}
  10070. FillChar(Result, SizeOf(Result), 0);
  10071. {Lock all small block types}
  10072. LockAllSmallBlockTypes;
  10073. {Lock the medium blocks}
  10074. LockMediumBlocks;
  10075. {Step through all the medium block pools}
  10076. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  10077. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  10078. begin
  10079. {Add to the total and committed address space}
  10080. Inc(Result.TotalAddrSpace, ((MediumBlockPoolSize + $ffff) and $ffff0000));
  10081. Inc(Result.TotalCommitted, ((MediumBlockPoolSize + $ffff) and $ffff0000));
  10082. {Add the medium block pool overhead}
  10083. Inc(Result.Overhead, (((MediumBlockPoolSize + $ffff) and $ffff0000)
  10084. - MediumBlockPoolSize + MediumBlockPoolHeaderSize));
  10085. {Get the first medium block in the pool}
  10086. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  10087. while LPMediumBlock <> nil do
  10088. begin
  10089. {Get the block header}
  10090. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  10091. {Get the block size}
  10092. LMediumBlockSize := LMediumBlockHeader and DropMediumAndLargeFlagsMask;
  10093. {Is the block in use?}
  10094. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  10095. begin
  10096. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  10097. begin
  10098. {Get the block type index}
  10099. LBlockTypeIndex := (UIntPtr(PSmallBlockPoolHeader(LPMediumBlock).BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
  10100. {Get the usage in the block}
  10101. LSmallBlockUsage := PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse
  10102. * SmallBlockTypes[LBlockTypeIndex].BlockSize;
  10103. {Get the total overhead for all the small blocks}
  10104. LSmallBlockOverhead := PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse
  10105. * (BlockHeaderSize{$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif});
  10106. {Add to the totals}
  10107. Inc(Result.FreeSmall, LMediumBlockSize - LSmallBlockUsage - BlockHeaderSize);
  10108. Inc(Result.Overhead, LSmallBlockOverhead + BlockHeaderSize);
  10109. Inc(Result.TotalAllocated, LSmallBlockUsage - LSmallBlockOverhead);
  10110. end
  10111. else
  10112. begin
  10113. {$ifdef FullDebugMode}
  10114. Dec(LMediumBlockSize, FullDebugBlockOverhead);
  10115. Inc(Result.Overhead, FullDebugBlockOverhead);
  10116. {$endif}
  10117. {Add to the result}
  10118. Inc(Result.TotalAllocated, LMediumBlockSize - BlockHeaderSize);
  10119. Inc(Result.Overhead, BlockHeaderSize);
  10120. end;
  10121. end
  10122. else
  10123. begin
  10124. {The medium block is free}
  10125. Inc(Result.FreeBig, LMediumBlockSize);
  10126. end;
  10127. {Next medium block}
  10128. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  10129. end;
  10130. {Get the next medium block pool}
  10131. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  10132. end;
  10133. {Add the sequential feed unused space}
  10134. Inc(Result.Unused, MediumSequentialFeedBytesLeft);
  10135. {Unlock the medium blocks}
  10136. MediumBlocksLocked := False;
  10137. {Unlock all the small block types}
  10138. for LInd := 0 to NumSmallBlockTypes - 1 do
  10139. SmallBlockTypes[LInd].BlockTypeLocked := False;
  10140. {Step through all the large blocks}
  10141. LockLargeBlocks;
  10142. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  10143. while LPLargeBlock <> @LargeBlocksCircularList do
  10144. begin
  10145. LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  10146. Inc(Result.TotalAddrSpace, LLargeBlockSize);
  10147. Inc(Result.TotalCommitted, LLargeBlockSize);
  10148. Inc(Result.TotalAllocated, LPLargeBlock.UserAllocatedSize
  10149. {$ifdef FullDebugMode} - FullDebugBlockOverhead{$endif});
  10150. Inc(Result.Overhead, LLargeBlockSize - LPLargeBlock.UserAllocatedSize
  10151. {$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif});
  10152. {Get the next large block}
  10153. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  10154. end;
  10155. LargeBlocksLocked := False;
  10156. {Set the total number of free bytes}
  10157. Result.TotalFree := Result.FreeSmall + Result.FreeBig + Result.Unused;
  10158. end;
  10159. {Frees all allocated memory. Does not support segmented large blocks (yet).}
  10160. procedure FreeAllMemory;
  10161. var
  10162. LPMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
  10163. LPMediumFreeBlock: PMediumFreeBlock;
  10164. LPLargeBlock, LPNextLargeBlock: PLargeBlockHeader;
  10165. LInd: Integer;
  10166. begin
  10167. {Free all block pools}
  10168. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  10169. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  10170. begin
  10171. {Get the next medium block pool so long}
  10172. LPNextMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  10173. {$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
  10174. FillChar(LPMediumBlockPoolHeader^, MediumBlockPoolSize, 0);
  10175. {$else}
  10176. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  10177. FillChar(LPMediumBlockPoolHeader^, MediumBlockPoolSize, 0);
  10178. {$endif}
  10179. {$endif}
  10180. {Free this pool}
  10181. VirtualFree(LPMediumBlockPoolHeader, 0, MEM_RELEASE);
  10182. {Next pool}
  10183. LPMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
  10184. end;
  10185. {Clear all small block types}
  10186. for LInd := 0 to High(SmallBlockTypes) do
  10187. begin
  10188. SmallBlockTypes[Lind].PreviousPartiallyFreePool := @SmallBlockTypes[Lind];
  10189. SmallBlockTypes[Lind].NextPartiallyFreePool := @SmallBlockTypes[Lind];
  10190. SmallBlockTypes[Lind].NextSequentialFeedBlockAddress := Pointer(1);
  10191. SmallBlockTypes[Lind].MaxSequentialFeedBlockAddress := nil;
  10192. end;
  10193. {Clear all medium block pools}
  10194. MediumBlockPoolsCircularList.PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  10195. MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  10196. {All medium bins are empty}
  10197. for LInd := 0 to High(MediumBlockBins) do
  10198. begin
  10199. LPMediumFreeBlock := @MediumBlockBins[LInd];
  10200. LPMediumFreeBlock.PreviousFreeBlock := LPMediumFreeBlock;
  10201. LPMediumFreeBlock.NextFreeBlock := LPMediumFreeBlock;
  10202. end;
  10203. MediumBlockBinGroupBitmap := 0;
  10204. FillChar(MediumBlockBinBitmaps, SizeOf(MediumBlockBinBitmaps), 0);
  10205. MediumSequentialFeedBytesLeft := 0;
  10206. {Free all large blocks}
  10207. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  10208. while LPLargeBlock <> @LargeBlocksCircularList do
  10209. begin
  10210. {Get the next large block}
  10211. LPNextLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  10212. {$ifdef ClearLargeBlocksBeforeReturningToOS}
  10213. FillChar(LPLargeBlock^,
  10214. LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask, 0);
  10215. {$endif}
  10216. {Free this large block}
  10217. VirtualFree(LPLargeBlock, 0, MEM_RELEASE);
  10218. {Next large block}
  10219. LPLargeBlock := LPNextLargeBlock;
  10220. end;
  10221. {There are no large blocks allocated}
  10222. LargeBlocksCircularList.PreviousLargeBlockHeader := @LargeBlocksCircularList;
  10223. LargeBlocksCircularList.NextLargeBlockHeader := @LargeBlocksCircularList;
  10224. end;
  10225. {----------------------------Memory Manager Setup-----------------------------}
  10226. {Checks that no other memory manager has been installed after the RTL MM and
  10227. that there are currently no live pointers allocated through the RTL MM.}
  10228. function CheckCanInstallMemoryManager: Boolean;
  10229. {$ifndef NoMessageBoxes}
  10230. var
  10231. LErrorMessageTitle: array[0..1023] of AnsiChar;
  10232. {$endif}
  10233. begin
  10234. {Default to error}
  10235. Result := False;
  10236. {$ifdef FullDebugMode}
  10237. {$ifdef LoadDebugDLLDynamically}
  10238. {$ifdef DoNotInstallIfDLLMissing}
  10239. {Should FastMM be installed only if the FastMM_FullDebugMode.dll file is
  10240. available?}
  10241. if FullDebugModeDLL = 0 then
  10242. Exit;
  10243. {$endif}
  10244. {$endif}
  10245. {$endif}
  10246. {Is FastMM already installed?}
  10247. if FastMMIsInstalled then
  10248. begin
  10249. {$ifdef UseOutputDebugString}
  10250. OutputDebugStringA(AlreadyInstalledMsg);
  10251. {$endif}
  10252. {$ifndef NoMessageBoxes}
  10253. AppendStringToModuleName(AlreadyInstalledTitle, LErrorMessageTitle);
  10254. ShowMessageBox(AlreadyInstalledMsg, LErrorMessageTitle);
  10255. {$endif}
  10256. Exit;
  10257. end;
  10258. {Has another MM been set, or has the Embarcadero MM been used? If so, this
  10259. file is not the first unit in the uses clause of the project's .dpr file.}
  10260. if IsMemoryManagerSet then
  10261. begin
  10262. {When using runtime packages, another library may already have installed
  10263. FastMM: Silently ignore the installation request.}
  10264. {$ifndef UseRuntimePackages}
  10265. {Another memory manager has been set.}
  10266. {$ifdef UseOutputDebugString}
  10267. OutputDebugStringA(OtherMMInstalledMsg);
  10268. {$endif}
  10269. {$ifndef NoMessageBoxes}
  10270. AppendStringToModuleName(OtherMMInstalledTitle, LErrorMessageTitle);
  10271. ShowMessageBox(OtherMMInstalledMsg, LErrorMessageTitle);
  10272. {$endif}
  10273. {$endif}
  10274. Exit;
  10275. end;
  10276. {$ifndef Linux}
  10277. if GetHeapStatus.TotalAllocated <> 0 then
  10278. begin
  10279. {Memory has been already been allocated with the RTL MM}
  10280. {$ifdef UseOutputDebugString}
  10281. OutputDebugStringA(MemoryAllocatedMsg);
  10282. {$endif}
  10283. {$ifndef NoMessageBoxes}
  10284. AppendStringToModuleName(MemoryAllocatedTitle, LErrorMessageTitle);
  10285. ShowMessageBox(MemoryAllocatedMsg, LErrorMessageTitle);
  10286. {$endif}
  10287. Exit;
  10288. end;
  10289. {$endif}
  10290. {All OK}
  10291. Result := True;
  10292. end;
  10293. {Initializes the lookup tables for the memory manager}
  10294. procedure InitializeMemoryManager;
  10295. const
  10296. {The size of the Inc(VMTIndex) code in TFreedObject.GetVirtualMethodIndex}
  10297. VMTIndexIncCodeSize = 6;
  10298. var
  10299. LInd, LSizeInd, LMinimumPoolSize, LOptimalPoolSize, LGroupNumber,
  10300. LBlocksPerPool, LPreviousBlockSize: Cardinal;
  10301. LPMediumFreeBlock: PMediumFreeBlock;
  10302. begin
  10303. {$ifdef FullDebugMode}
  10304. {$ifdef LoadDebugDLLDynamically}
  10305. {Attempt to load the FullDebugMode DLL dynamically.}
  10306. FullDebugModeDLL := LoadLibrary(FullDebugModeLibraryName);
  10307. if FullDebugModeDLL <> 0 then
  10308. begin
  10309. GetStackTrace := GetProcAddress(FullDebugModeDLL,
  10310. {$ifdef RawStackTraces}'GetRawStackTrace'{$else}'GetFrameBasedStackTrace'{$endif});
  10311. LogStackTrace := GetProcAddress(FullDebugModeDLL, 'LogStackTrace');
  10312. end;
  10313. {$endif}
  10314. {$endif}
  10315. {$ifdef EnableMMX}
  10316. {$ifndef ForceMMX}
  10317. UseMMX := MMX_Supported;
  10318. {$endif}
  10319. {$endif}
  10320. {Initialize the memory manager}
  10321. {-------------Set up the small block types-------------}
  10322. LPreviousBlockSize := 0;
  10323. for LInd := 0 to High(SmallBlockTypes) do
  10324. begin
  10325. {Set the move procedure}
  10326. {$ifdef UseCustomFixedSizeMoveRoutines}
  10327. {The upsize move procedure may move chunks in 16 bytes even with 8-byte
  10328. alignment, since the new size will always be at least 8 bytes bigger than
  10329. the old size.}
  10330. if not Assigned(SmallBlockTypes[LInd].UpsizeMoveProcedure) then
  10331. {$ifdef UseCustomVariableSizeMoveRoutines}
  10332. SmallBlockTypes[LInd].UpsizeMoveProcedure := MoveX16LP;
  10333. {$else}
  10334. SmallBlockTypes[LInd].UpsizeMoveProcedure := @System.Move;
  10335. {$endif}
  10336. {$endif}
  10337. {Set the first "available pool" to the block type itself, so that the
  10338. allocation routines know that there are currently no pools with free
  10339. blocks of this size.}
  10340. SmallBlockTypes[LInd].PreviousPartiallyFreePool := @SmallBlockTypes[LInd];
  10341. SmallBlockTypes[LInd].NextPartiallyFreePool := @SmallBlockTypes[LInd];
  10342. {Set the block size to block type index translation table}
  10343. for LSizeInd := (LPreviousBlockSize div SmallBlockGranularity) to ((SmallBlockTypes[LInd].BlockSize - 1) div SmallBlockGranularity) do
  10344. AllocSize2SmallBlockTypeIndX4[LSizeInd] := LInd * 4;
  10345. {Cannot sequential feed yet: Ensure that the next address is greater than
  10346. the maximum address}
  10347. SmallBlockTypes[LInd].MaxSequentialFeedBlockAddress := Pointer(0);
  10348. SmallBlockTypes[LInd].NextSequentialFeedBlockAddress := Pointer(1);
  10349. {Get the mask to use for finding a medium block suitable for a block pool}
  10350. LMinimumPoolSize :=
  10351. ((SmallBlockTypes[LInd].BlockSize * MinimumSmallBlocksPerPool
  10352. + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)
  10353. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  10354. if LMinimumPoolSize < MinimumMediumBlockSize then
  10355. LMinimumPoolSize := MinimumMediumBlockSize;
  10356. {Get the closest group number for the minimum pool size}
  10357. LGroupNumber := (LMinimumPoolSize - MinimumMediumBlockSize + MediumBlockBinsPerGroup * MediumBlockGranularity div 2)
  10358. div (MediumBlockBinsPerGroup * MediumBlockGranularity);
  10359. {Too large?}
  10360. if LGroupNumber > 7 then
  10361. LGroupNumber := 7;
  10362. {Set the bitmap}
  10363. SmallBlockTypes[LInd].AllowedGroupsForBlockPoolBitmap := Byte(-(1 shl LGroupNumber));
  10364. {Set the minimum pool size}
  10365. SmallBlockTypes[LInd].MinimumBlockPoolSize := MinimumMediumBlockSize + LGroupNumber * (MediumBlockBinsPerGroup * MediumBlockGranularity);
  10366. {Get the optimal block pool size}
  10367. LOptimalPoolSize := ((SmallBlockTypes[LInd].BlockSize * TargetSmallBlocksPerPool
  10368. + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)
  10369. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  10370. {Limit the optimal pool size to within range}
  10371. if LOptimalPoolSize < OptimalSmallBlockPoolSizeLowerLimit then
  10372. LOptimalPoolSize := OptimalSmallBlockPoolSizeLowerLimit;
  10373. if LOptimalPoolSize > OptimalSmallBlockPoolSizeUpperLimit then
  10374. LOptimalPoolSize := OptimalSmallBlockPoolSizeUpperLimit;
  10375. {How many blocks will fit in the adjusted optimal size?}
  10376. LBlocksPerPool := (LOptimalPoolSize - SmallBlockPoolHeaderSize) div SmallBlockTypes[LInd].BlockSize;
  10377. {Recalculate the optimal pool size to minimize wastage due to a partial
  10378. last block.}
  10379. SmallBlockTypes[LInd].OptimalBlockPoolSize :=
  10380. ((LBlocksPerPool * SmallBlockTypes[LInd].BlockSize + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset) and -MediumBlockGranularity) + MediumBlockSizeOffset;
  10381. {$ifdef CheckHeapForCorruption}
  10382. {Debug checks}
  10383. if (SmallBlockTypes[LInd].OptimalBlockPoolSize < MinimumMediumBlockSize)
  10384. or (SmallBlockTypes[LInd].BlockSize div SmallBlockGranularity * SmallBlockGranularity <> SmallBlockTypes[LInd].BlockSize) then
  10385. begin
  10386. {$ifdef BCB6OrDelphi7AndUp}
  10387. System.Error(reInvalidPtr);
  10388. {$else}
  10389. System.RunError(reInvalidPtr);
  10390. {$endif}
  10391. end;
  10392. {$endif}
  10393. {Set the previous small block size}
  10394. LPreviousBlockSize := SmallBlockTypes[LInd].BlockSize;
  10395. end;
  10396. {-------------------Set up the medium blocks-------------------}
  10397. {$ifdef CheckHeapForCorruption}
  10398. {Check that there are no gaps between where the small blocks end and the
  10399. medium blocks start}
  10400. if (((MaximumSmallBlockSize - 3) + (MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset))
  10401. and -MediumBlockGranularity) + MediumBlockSizeOffset < MinimumMediumBlockSize then
  10402. begin
  10403. {$ifdef BCB6OrDelphi7AndUp}
  10404. System.Error(reInvalidPtr);
  10405. {$else}
  10406. System.RunError(reInvalidPtr);
  10407. {$endif}
  10408. end;
  10409. {$endif}
  10410. {There are currently no medium block pools}
  10411. MediumBlockPoolsCircularList.PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  10412. MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  10413. {All medium bins are empty}
  10414. for LInd := 0 to High(MediumBlockBins) do
  10415. begin
  10416. LPMediumFreeBlock := @MediumBlockBins[LInd];
  10417. LPMediumFreeBlock.PreviousFreeBlock := LPMediumFreeBlock;
  10418. LPMediumFreeBlock.NextFreeBlock := LPMediumFreeBlock;
  10419. end;
  10420. {------------------Set up the large blocks---------------------}
  10421. LargeBlocksCircularList.PreviousLargeBlockHeader := @LargeBlocksCircularList;
  10422. LargeBlocksCircularList.NextLargeBlockHeader := @LargeBlocksCircularList;
  10423. {------------------Set up the debugging structures---------------------}
  10424. {$ifdef FullDebugMode}
  10425. {Set up the fake VMT}
  10426. {Copy the basic info from the TFreedObject class}
  10427. System.Move(Pointer(PByte(TFreedObject) + vmtSelfPtr + SizeOf(Pointer))^,
  10428. FreedObjectVMT.VMTData[vmtSelfPtr + SizeOf(Pointer)], vmtParent - vmtSelfPtr);
  10429. PNativeUInt(@FreedObjectVMT.VMTData[vmtSelfPtr])^ := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
  10430. {Set up the virtual method table}
  10431. for LInd := 0 to MaxFakeVMTEntries - 1 do
  10432. begin
  10433. PNativeUInt(@FreedObjectVMT.VMTMethods[Low(FreedObjectVMT.VMTMethods) + Integer(LInd * SizeOf(Pointer))])^ :=
  10434. NativeUInt(@TFreedObject.GetVirtualMethodIndex) + LInd * VMTIndexIncCodeSize;
  10435. {$ifdef CatchUseOfFreedInterfaces}
  10436. VMTBadInterface[LInd] := @TFreedObject.InterfaceError;
  10437. {$endif}
  10438. end;
  10439. {Set up the default log file name}
  10440. SetDefaultMMLogFileName;
  10441. {$endif}
  10442. end;
  10443. {Installs the memory manager (InitializeMemoryManager should be called first)}
  10444. procedure InstallMemoryManager;
  10445. {$ifdef MMSharingEnabled}
  10446. var
  10447. i, LCurrentProcessID: Cardinal;
  10448. LPMapAddress: PPointer;
  10449. LChar: AnsiChar;
  10450. {$endif}
  10451. begin
  10452. if not FastMMIsInstalled then
  10453. begin
  10454. {$ifdef FullDebugMode}
  10455. {$ifdef 32Bit}
  10456. {Try to reserve the 64K block covering address $80808080}
  10457. ReservedBlock := VirtualAlloc(Pointer(DebugReservedAddress), 65536, MEM_RESERVE, PAGE_NOACCESS);
  10458. {$endif}
  10459. {$endif}
  10460. {$ifdef MMSharingEnabled}
  10461. {Build a string identifying the current process}
  10462. LCurrentProcessID := GetCurrentProcessId;
  10463. for i := 0 to 7 do
  10464. begin
  10465. LChar := HexTable[((LCurrentProcessID shr (i * 4)) and $F)];
  10466. MappingObjectName[(High(MappingObjectName) - 1) - i] := LChar;
  10467. {$ifdef EnableBackwardCompatibleMMSharing}
  10468. UniqueProcessIDString[8 - i] := LChar;
  10469. UniqueProcessIDStringBE[8 - i] := LChar;
  10470. {$endif}
  10471. end;
  10472. {$endif}
  10473. {$ifdef AttemptToUseSharedMM}
  10474. {Is the replacement memory manager already installed for this process?}
  10475. {$ifdef EnableBackwardCompatibleMMSharing}
  10476. MMWindow := FindWindowA('STATIC', PAnsiChar(@UniqueProcessIDString[1]));
  10477. MMWindowBE := FindWindowA('STATIC', PAnsiChar(@UniqueProcessIDStringBE[1]));
  10478. {$endif}
  10479. MappingObjectHandle := OpenFileMappingA(FILE_MAP_READ, False, MappingObjectName);
  10480. {Is no MM being shared?}
  10481. {$ifdef EnableBackwardCompatibleMMSharing}
  10482. if (MMWindow or MMWindowBE or MappingObjectHandle) = 0 then
  10483. {$else}
  10484. if MappingObjectHandle = 0 then
  10485. {$endif}
  10486. begin
  10487. {$endif}
  10488. {$ifdef ShareMM}
  10489. {Share the MM with other DLLs? - if this DLL is unloaded, then
  10490. dependent DLLs will cause a crash.}
  10491. {$ifndef ShareMMIfLibrary}
  10492. if not IsLibrary then
  10493. {$endif}
  10494. begin
  10495. {$ifdef EnableBackwardCompatibleMMSharing}
  10496. {No memory manager installed yet - create the invisible window}
  10497. MMWindow := CreateWindowA('STATIC', PAnsiChar(@UniqueProcessIDString[1]),
  10498. WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
  10499. MMWindowBE := CreateWindowA('STATIC', PAnsiChar(@UniqueProcessIDStringBE[1]),
  10500. WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
  10501. {The window data is a pointer to this memory manager}
  10502. if MMWindow <> 0 then
  10503. SetWindowLongA(MMWindow, GWL_USERDATA, NativeInt(@NewMemoryManager));
  10504. if MMWindowBE <> 0 then
  10505. SetWindowLongA(MMWindowBE, GWL_USERDATA, NativeInt(@NewMemoryManager));
  10506. {$endif}
  10507. {Create the memory mapped file}
  10508. MappingObjectHandle := CreateFileMappingA(INVALID_HANDLE_VALUE, nil,
  10509. PAGE_READWRITE, 0, SizeOf(Pointer), MappingObjectName);
  10510. {Map a view of the memory}
  10511. LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_WRITE, 0, 0, 0);
  10512. {Set a pointer to the new memory manager}
  10513. LPMapAddress^ := @NewMemoryManager;
  10514. {Unmap the file}
  10515. UnmapViewOfFile(LPMapAddress);
  10516. end;
  10517. {$endif}
  10518. {We will be using this memory manager}
  10519. {$ifndef FullDebugMode}
  10520. NewMemoryManager.GetMem := FastGetMem;
  10521. NewMemoryManager.FreeMem := FastFreeMem;
  10522. NewMemoryManager.ReallocMem := FastReallocMem;
  10523. {$else}
  10524. NewMemoryManager.GetMem := DebugGetMem;
  10525. NewMemoryManager.FreeMem := DebugFreeMem;
  10526. NewMemoryManager.ReallocMem := DebugReallocMem;
  10527. {$endif}
  10528. {$ifdef BDS2006AndUp}
  10529. {$ifndef FullDebugMode}
  10530. NewMemoryManager.AllocMem := FastAllocMem;
  10531. {$else}
  10532. NewMemoryManager.AllocMem := DebugAllocMem;
  10533. {$endif}
  10534. {$ifdef EnableMemoryLeakReporting}
  10535. NewMemoryManager.RegisterExpectedMemoryLeak := RegisterExpectedMemoryLeak;
  10536. NewMemoryManager.UnRegisterExpectedMemoryLeak := UnRegisterExpectedMemoryLeak;
  10537. {$else}
  10538. NewMemoryManager.RegisterExpectedMemoryLeak := NoOpRegisterExpectedMemoryLeak;
  10539. NewMemoryManager.UnRegisterExpectedMemoryLeak := NoOpUnRegisterExpectedMemoryLeak;
  10540. {$endif}
  10541. {$endif}
  10542. {Owns the memory manager}
  10543. IsMemoryManagerOwner := True;
  10544. {$ifdef AttemptToUseSharedMM}
  10545. end
  10546. else
  10547. begin
  10548. {Get the address of the shared memory manager}
  10549. {$ifndef BDS2006AndUp}
  10550. {$ifdef EnableBackwardCompatibleMMSharing}
  10551. if MappingObjectHandle <> 0 then
  10552. begin
  10553. {$endif}
  10554. {Map a view of the memory}
  10555. LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_READ, 0, 0, 0);
  10556. {Set the new memory manager}
  10557. NewMemoryManager := PMemoryManager(LPMapAddress^)^;
  10558. {Unmap the file}
  10559. UnmapViewOfFile(LPMapAddress);
  10560. {$ifdef EnableBackwardCompatibleMMSharing}
  10561. end
  10562. else
  10563. begin
  10564. if MMWindow <> 0 then
  10565. begin
  10566. NewMemoryManager := PMemoryManager(GetWindowLong(MMWindow, GWL_USERDATA))^;
  10567. end
  10568. else
  10569. begin
  10570. NewMemoryManager := PMemoryManager(GetWindowLong(MMWindowBE, GWL_USERDATA))^;
  10571. end;
  10572. end;
  10573. {$endif}
  10574. {$else}
  10575. {$ifdef EnableBackwardCompatibleMMSharing}
  10576. if MappingObjectHandle <> 0 then
  10577. begin
  10578. {$endif}
  10579. {Map a view of the memory}
  10580. LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_READ, 0, 0, 0);
  10581. {Set the new memory manager}
  10582. NewMemoryManager := PMemoryManagerEx(LPMapAddress^)^;
  10583. {Unmap the file}
  10584. UnmapViewOfFile(LPMapAddress);
  10585. {$ifdef EnableBackwardCompatibleMMSharing}
  10586. end
  10587. else
  10588. begin
  10589. if MMWindow <> 0 then
  10590. begin
  10591. NewMemoryManager := PMemoryManagerEx(GetWindowLong(MMWindow, GWL_USERDATA))^;
  10592. end
  10593. else
  10594. begin
  10595. NewMemoryManager := PMemoryManagerEx(GetWindowLong(MMWindowBE, GWL_USERDATA))^;
  10596. end;
  10597. end;
  10598. {$endif}
  10599. {$endif}
  10600. {Close the file mapping handle}
  10601. CloseHandle(MappingObjectHandle);
  10602. MappingObjectHandle := 0;
  10603. {The memory manager is not owned by this module}
  10604. IsMemoryManagerOwner := False;
  10605. end;
  10606. {$endif}
  10607. {Save the old memory manager}
  10608. GetMemoryManager(OldMemoryManager);
  10609. {Replace the memory manager with either this one or the shared one.}
  10610. SetMemoryManager(NewMemoryManager);
  10611. {FastMM is now installed}
  10612. FastMMIsInstalled := True;
  10613. {$ifdef UseOutputDebugString}
  10614. if IsMemoryManagerOwner then
  10615. OutputDebugStringA(FastMMInstallMsg)
  10616. else
  10617. OutputDebugStringA(FastMMInstallSharedMsg);
  10618. {$endif}
  10619. end;
  10620. end;
  10621. procedure UninstallMemoryManager;
  10622. begin
  10623. {Is this the owner of the shared MM window?}
  10624. if IsMemoryManagerOwner then
  10625. begin
  10626. {$ifdef ShareMM}
  10627. {$ifdef EnableBackwardCompatibleMMSharing}
  10628. {Destroy the window}
  10629. if MMWindow <> 0 then
  10630. begin
  10631. DestroyWindow(MMWindow);
  10632. MMWindow := 0;
  10633. end;
  10634. if MMWindowBE <> 0 then
  10635. begin
  10636. DestroyWindow(MMWindowBE);
  10637. MMWindowBE := 0;
  10638. end;
  10639. {$endif}
  10640. {Destroy the memory mapped file handle}
  10641. if MappingObjectHandle <> 0 then
  10642. begin
  10643. CloseHandle(MappingObjectHandle);
  10644. MappingObjectHandle := 0;
  10645. end;
  10646. {$endif}
  10647. {$ifdef FullDebugMode}
  10648. {Release the reserved block}
  10649. if ReservedBlock <> nil then
  10650. begin
  10651. VirtualFree(ReservedBlock, 0, MEM_RELEASE);
  10652. ReservedBlock := nil;
  10653. end;
  10654. {$endif}
  10655. end;
  10656. {$ifndef DetectMMOperationsAfterUninstall}
  10657. {Restore the old memory manager}
  10658. SetMemoryManager(OldMemoryManager);
  10659. {$else}
  10660. {Set the invalid memory manager: no more MM operations allowed}
  10661. SetMemoryManager(InvalidMemoryManager);
  10662. {$endif}
  10663. {Memory manager has been uninstalled}
  10664. FastMMIsInstalled := False;
  10665. {$ifdef UseOutputDebugString}
  10666. if IsMemoryManagerOwner then
  10667. OutputDebugStringA(FastMMuninstallMsg)
  10668. else
  10669. OutputDebugStringA(FastMMUninstallSharedMsg);
  10670. {$endif}
  10671. end;
  10672. procedure FinalizeMemoryManager;
  10673. begin
  10674. {Restore the old memory manager if FastMM has been installed}
  10675. if FastMMIsInstalled then
  10676. begin
  10677. {$ifndef NeverUninstall}
  10678. {Uninstall FastMM}
  10679. UninstallMemoryManager;
  10680. {$endif}
  10681. {Do we own the memory manager, or are we just sharing it?}
  10682. if IsMemoryManagerOwner then
  10683. begin
  10684. {$ifdef CheckUseOfFreedBlocksOnShutdown}
  10685. CheckBlocksOnShutdown(
  10686. {$ifdef EnableMemoryLeakReporting}
  10687. True
  10688. {$ifdef RequireIDEPresenceForLeakReporting}
  10689. and DelphiIsRunning
  10690. {$endif}
  10691. {$ifdef RequireDebuggerPresenceForLeakReporting}
  10692. and ((DebugHook <> 0)
  10693. {$ifdef PatchBCBTerminate}
  10694. or (Assigned(pCppDebugHook) and (pCppDebugHook^ <> 0))
  10695. {$endif PatchBCBTerminate}
  10696. )
  10697. {$endif}
  10698. {$ifdef ManualLeakReportingControl}
  10699. and ReportMemoryLeaksOnShutdown
  10700. {$endif}
  10701. {$else}
  10702. False
  10703. {$endif}
  10704. );
  10705. {$else}
  10706. {$ifdef EnableMemoryLeakReporting}
  10707. if True
  10708. {$ifdef RequireIDEPresenceForLeakReporting}
  10709. and DelphiIsRunning
  10710. {$endif}
  10711. {$ifdef RequireDebuggerPresenceForLeakReporting}
  10712. and ((DebugHook <> 0)
  10713. {$ifdef PatchBCBTerminate}
  10714. or (Assigned(pCppDebugHook) and (pCppDebugHook^ <> 0))
  10715. {$endif PatchBCBTerminate}
  10716. )
  10717. {$endif}
  10718. {$ifdef ManualLeakReportingControl}
  10719. and ReportMemoryLeaksOnShutdown
  10720. {$endif}
  10721. then
  10722. CheckBlocksOnShutdown(True);
  10723. {$endif}
  10724. {$endif}
  10725. {$ifdef EnableMemoryLeakReporting}
  10726. {Free the expected memory leaks list}
  10727. if ExpectedMemoryLeaks <> nil then
  10728. begin
  10729. VirtualFree(ExpectedMemoryLeaks, 0, MEM_RELEASE);
  10730. ExpectedMemoryLeaks := nil;
  10731. end;
  10732. {$endif}
  10733. {$ifndef NeverUninstall}
  10734. {Clean up: Free all memory. If this is a .DLL that owns its own MM, then
  10735. it is necessary to prevent the main application from running out of
  10736. address space.}
  10737. FreeAllMemory;
  10738. {$endif}
  10739. end;
  10740. end;
  10741. end;
  10742. procedure RunInitializationCode;
  10743. begin
  10744. {Only run this code once during startup.}
  10745. if InitializationCodeHasRun then
  10746. Exit;
  10747. InitializationCodeHasRun := True;
  10748. {$ifndef BCB}
  10749. {$ifdef InstallOnlyIfRunningInIDE}
  10750. if (DebugHook <> 0) and DelphiIsRunning then
  10751. {$endif}
  10752. begin
  10753. {Initialize all the lookup tables, etc. for the memory manager}
  10754. InitializeMemoryManager;
  10755. {Has another MM been set, or has the Embarcadero MM been used? If so, this
  10756. file is not the first unit in the uses clause of the project's .dpr
  10757. file.}
  10758. if CheckCanInstallMemoryManager then
  10759. begin
  10760. {$ifdef ClearLogFileOnStartup}
  10761. DeleteEventLog;
  10762. {$endif}
  10763. InstallMemoryManager;
  10764. end;
  10765. end;
  10766. {$endif}
  10767. end;
  10768. initialization
  10769. RunInitializationCode;
  10770. finalization
  10771. {$ifndef PatchBCBTerminate}
  10772. FinalizeMemoryManager;
  10773. {$endif}
  10774. end.