JclDebug.pas 259 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109
  1. {**************************************************************************************************}
  2. { }
  3. { Project JEDI Code Library (JCL) }
  4. { }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the }
  7. { License at http://www.mozilla.org/MPL/ }
  8. { }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights }
  11. { and limitations under the License. }
  12. { }
  13. { The Original Code is JclDebug.pas. }
  14. { }
  15. { The Initial Developers of the Original Code are Petr Vones and Marcel van Brakel. }
  16. { Portions created by these individuals are Copyright (C) of these individuals. }
  17. { All Rights Reserved. }
  18. { }
  19. { Contributor(s): }
  20. { Marcel van Brakel }
  21. { Flier Lu (flier) }
  22. { Florent Ouchet (outchy) }
  23. { Robert Marquardt (marquardt) }
  24. { Robert Rossmair (rrossmair) }
  25. { Andreas Hausladen (ahuser) }
  26. { Petr Vones (pvones) }
  27. { Soeren Muehlbauer }
  28. { Uwe Schuster (uschuster) }
  29. { }
  30. {**************************************************************************************************}
  31. { }
  32. { Various debugging support routines and classes. This includes: Diagnostics routines, Trace }
  33. { routines, Stack tracing and Source Locations a la the C/C++ __FILE__ and __LINE__ macros. }
  34. { }
  35. {**************************************************************************************************}
  36. { }
  37. { Last modified: $Date:: $ }
  38. { Revision: $Rev:: $ }
  39. { Author: $Author:: $ }
  40. { }
  41. {**************************************************************************************************}
  42. unit JclDebug;
  43. interface
  44. {$I jcl.inc}
  45. {$I windowsonly.inc}
  46. uses
  47. {$IFDEF UNITVERSIONING}
  48. JclUnitVersioning,
  49. {$ENDIF UNITVERSIONING}
  50. {$IFDEF HAS_UNITSCOPE}
  51. {$IFDEF MSWINDOWS}
  52. Winapi.Windows,
  53. {$ENDIF MSWINDOWS}
  54. System.Classes, System.SysUtils, System.Contnrs,
  55. {$ELSE ~HAS_UNITSCOPE}
  56. {$IFDEF MSWINDOWS}
  57. Windows,
  58. {$ENDIF MSWINDOWS}
  59. Classes, SysUtils, Contnrs,
  60. {$ENDIF ~HAS_UNITSCOPE}
  61. JclBase, JclFileUtils, JclPeImage,
  62. {$IFDEF BORLAND}
  63. JclTD32,
  64. {$ENDIF BORLAND}
  65. JclSynch;
  66. // Diagnostics
  67. procedure AssertKindOf(const ClassName: string; const Obj: TObject); overload;
  68. procedure AssertKindOf(const ClassType: TClass; const Obj: TObject); overload;
  69. // use TraceMsg
  70. // procedure Trace(const Msg: string);
  71. procedure TraceMsg(const Msg: string);
  72. procedure TraceFmt(const Fmt: string; const Args: array of const);
  73. procedure TraceLoc(const Msg: string);
  74. procedure TraceLocFmt(const Fmt: string; const Args: array of const);
  75. // Optimized functionality of JclSysInfo functions ModuleFromAddr and IsSystemModule
  76. type
  77. TJclModuleInfo = class(TObject)
  78. private
  79. FSize: Cardinal;
  80. FEndAddr: Pointer;
  81. FStartAddr: Pointer;
  82. FSystemModule: Boolean;
  83. public
  84. property EndAddr: Pointer read FEndAddr;
  85. property Size: Cardinal read FSize;
  86. property StartAddr: Pointer read FStartAddr;
  87. property SystemModule: Boolean read FSystemModule;
  88. end;
  89. TJclModuleInfoList = class(TObjectList)
  90. private
  91. FDynamicBuild: Boolean;
  92. FSystemModulesOnly: Boolean;
  93. FRefCount: Integer;
  94. function GetItems(Index: Integer): TJclModuleInfo;
  95. function GetModuleFromAddress(Addr: Pointer): TJclModuleInfo;
  96. protected
  97. procedure BuildModulesList;
  98. function CreateItemForAddress(Addr: Pointer; SystemModule: Boolean): TJclModuleInfo;
  99. public
  100. constructor Create(ADynamicBuild, ASystemModulesOnly: Boolean);
  101. function AddModule(Module: HMODULE; SystemModule: Boolean): Boolean;
  102. function IsSystemModuleAddress(Addr: Pointer): Boolean;
  103. function IsValidModuleAddress(Addr: Pointer): Boolean;
  104. property DynamicBuild: Boolean read FDynamicBuild;
  105. property Items[Index: Integer]: TJclModuleInfo read GetItems;
  106. property ModuleFromAddress[Addr: Pointer]: TJclModuleInfo read GetModuleFromAddress;
  107. end;
  108. function JclValidateModuleAddress(Addr: Pointer): Boolean;
  109. // MAP file abstract parser
  110. type
  111. PJclMapAddress = ^TJclMapAddress;
  112. TJclMapAddress = packed record
  113. Segment: Word;
  114. Offset: TJclAddr;
  115. end;
  116. PJclMapString = PAnsiChar;
  117. TJclAbstractMapParser = class(TObject)
  118. private
  119. FLinkerBug: Boolean;
  120. FLinkerBugUnitName: PJclMapString;
  121. FStream: TJclFileMappingStream;
  122. function GetLinkerBugUnitName: string;
  123. protected
  124. FModule: HMODULE;
  125. FLastUnitName: PJclMapString;
  126. FLastUnitFileName: PJclMapString;
  127. procedure ClassTableItem(const Address: TJclMapAddress; Len: Integer; SectionName, GroupName: PJclMapString); virtual; abstract;
  128. procedure SegmentItem(const Address: TJclMapAddress; Len: Integer; GroupName, UnitName: PJclMapString); virtual; abstract;
  129. function CanHandlePublicsByName: Boolean; virtual; abstract;
  130. function CanHandlePublicsByValue: Boolean; virtual; abstract;
  131. procedure PublicsByNameItem(const Address: TJclMapAddress; Name: PJclMapString); virtual; abstract;
  132. procedure PublicsByValueItem(const Address: TJclMapAddress; Name: PJclMapString); virtual; abstract;
  133. procedure LineNumberUnitItem(UnitName, UnitFileName: PJclMapString); virtual; abstract;
  134. procedure LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress); virtual; abstract;
  135. public
  136. constructor Create(const MapFileName: TFileName; Module: HMODULE); overload; virtual;
  137. constructor Create(const MapFileName: TFileName); overload;
  138. destructor Destroy; override;
  139. procedure Parse;
  140. class function MapStringToFileName(MapString: PJclMapString): string;
  141. class function MapStringToModuleName(MapString: PJclMapString): string;
  142. class function MapStringToStr(MapString: PJclMapString; IgnoreSpaces: Boolean = False): string;
  143. property LinkerBug: Boolean read FLinkerBug;
  144. property LinkerBugUnitName: string read GetLinkerBugUnitName;
  145. property Stream: TJclFileMappingStream read FStream;
  146. end;
  147. // MAP file parser
  148. TJclMapClassTableEvent = procedure(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const SectionName, GroupName: string) of object;
  149. TJclMapSegmentEvent = procedure(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const GroupName, UnitName: string) of object;
  150. TJclMapPublicsEvent = procedure(Sender: TObject; const Address: TJclMapAddress; const Name: string) of object;
  151. TJclMapLineNumberUnitEvent = procedure(Sender: TObject; const UnitName, UnitFileName: string) of object;
  152. TJclMapLineNumbersEvent = procedure(Sender: TObject; LineNumber: Integer; const Address: TJclMapAddress) of object;
  153. TJclMapParser = class(TJclAbstractMapParser)
  154. private
  155. FOnClassTable: TJclMapClassTableEvent;
  156. FOnLineNumbers: TJclMapLineNumbersEvent;
  157. FOnLineNumberUnit: TJclMapLineNumberUnitEvent;
  158. FOnPublicsByValue: TJclMapPublicsEvent;
  159. FOnPublicsByName: TJclMapPublicsEvent;
  160. FOnSegmentItem: TJclMapSegmentEvent;
  161. protected
  162. procedure ClassTableItem(const Address: TJclMapAddress; Len: Integer; SectionName, GroupName: PJclMapString); override;
  163. procedure SegmentItem(const Address: TJclMapAddress; Len: Integer; GroupName, UnitName: PJclMapString); override;
  164. function CanHandlePublicsByName: Boolean; override;
  165. function CanHandlePublicsByValue: Boolean; override;
  166. procedure PublicsByNameItem(const Address: TJclMapAddress; Name: PJclMapString); override;
  167. procedure PublicsByValueItem(const Address: TJclMapAddress; Name: PJclMapString); override;
  168. procedure LineNumberUnitItem(UnitName, UnitFileName: PJclMapString); override;
  169. procedure LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress); override;
  170. public
  171. property OnClassTable: TJclMapClassTableEvent read FOnClassTable write FOnClassTable;
  172. property OnSegment: TJclMapSegmentEvent read FOnSegmentItem write FOnSegmentItem;
  173. property OnPublicsByName: TJclMapPublicsEvent read FOnPublicsByName write FOnPublicsByName;
  174. property OnPublicsByValue: TJclMapPublicsEvent read FOnPublicsByValue write FOnPublicsByValue;
  175. property OnLineNumberUnit: TJclMapLineNumberUnitEvent read FOnLineNumberUnit write FOnLineNumberUnit;
  176. property OnLineNumbers: TJclMapLineNumbersEvent read FOnLineNumbers write FOnLineNumbers;
  177. end;
  178. TJclMapStringCache = record
  179. CachedValue: string;
  180. RawValue: PJclMapString;
  181. TLS: Boolean;
  182. end;
  183. // MAP file scanner
  184. PJclMapSegmentClass = ^TJclMapSegmentClass;
  185. TJclMapSegmentClass = record
  186. Segment: Word; // segment ID
  187. Start: DWORD; // start as in the map file
  188. Addr: DWORD; // start as in process memory
  189. VA: DWORD; // position relative to module base adress
  190. Len: DWORD; // segment length
  191. SectionName: TJclMapStringCache;
  192. GroupName: TJclMapStringCache;
  193. end;
  194. PJclMapSegment = ^TJclMapSegment;
  195. TJclMapSegment = record
  196. Segment: Word;
  197. StartVA: DWORD; // VA relative to (module base address + $10000)
  198. EndVA: DWORD;
  199. UnitName: TJclMapStringCache;
  200. end;
  201. PJclMapProcName = ^TJclMapProcName;
  202. TJclMapProcName = record
  203. Segment: Word;
  204. VA: DWORD; // VA relative to (module base address + $10000)
  205. ProcName: TJclMapStringCache;
  206. end;
  207. PJclMapLineNumber = ^TJclMapLineNumber;
  208. TJclMapLineNumber = record
  209. Segment: Word;
  210. VA: DWORD; // VA relative to (module base address + $10000)
  211. LineNumber: Integer;
  212. UnitName: PJclMapString;
  213. end;
  214. TJclMapScanner = class(TJclAbstractMapParser)
  215. private
  216. FSegmentClasses: array of TJclMapSegmentClass;
  217. FLineNumbers: array of TJclMapLineNumber;
  218. FProcNames: array of TJclMapProcName;
  219. FSegments: array of TJclMapSegment;
  220. FSourceNames: array of TJclMapProcName;
  221. FLineNumbersCnt: Integer;
  222. FLineNumberErrors: Integer;
  223. FNewUnitFileName: PJclMapString;
  224. FCurrentUnitName: PJclMapString;
  225. FProcNamesCnt: Integer;
  226. FSegmentCnt: Integer;
  227. FLastAccessedSegementIndex: Integer;
  228. function IndexOfSegment(Addr: DWORD): Integer;
  229. protected
  230. function MAPAddrToVA(const Addr: DWORD): DWORD;
  231. procedure ClassTableItem(const Address: TJclMapAddress; Len: Integer; SectionName, GroupName: PJclMapString); override;
  232. procedure SegmentItem(const Address: TJclMapAddress; Len: Integer; GroupName, UnitName: PJclMapString); override;
  233. function CanHandlePublicsByName: Boolean; override;
  234. function CanHandlePublicsByValue: Boolean; override;
  235. procedure PublicsByNameItem(const Address: TJclMapAddress; Name: PJclMapString); override;
  236. procedure PublicsByValueItem(const Address: TJclMapAddress; Name: PJclMapString); override;
  237. procedure LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress); override;
  238. procedure LineNumberUnitItem(UnitName, UnitFileName: PJclMapString); override;
  239. procedure Scan;
  240. function GetLineNumberByIndex(Index: Integer): TJCLMapLineNumber;
  241. public
  242. constructor Create(const MapFileName: TFileName; Module: HMODULE); override;
  243. class function MapStringCacheToFileName(var MapString: TJclMapStringCache): string;
  244. class function MapStringCacheToModuleName(var MapString: TJclMapStringCache): string;
  245. class function MapStringCacheToStr(var MapString: TJclMapStringCache; IgnoreSpaces: Boolean = False): string;
  246. // Addr are virtual addresses relative to (module base address + $10000)
  247. function LineNumberFromAddr(Addr: DWORD): Integer; overload;
  248. function LineNumberFromAddr(Addr: DWORD; out Offset: Integer): Integer; overload;
  249. function ModuleNameFromAddr(Addr: DWORD): string;
  250. function ModuleStartFromAddr(Addr: DWORD): DWORD;
  251. function ProcNameFromAddr(Addr: DWORD): string; overload;
  252. function ProcNameFromAddr(Addr: DWORD; out Offset: Integer): string; overload;
  253. function SourceNameFromAddr(Addr: DWORD): string;
  254. function VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;
  255. property LineNumberErrors: Integer read FLineNumberErrors;
  256. property LineNumbersCnt: Integer read FLineNumbersCnt;
  257. property LineNumberByIndex[Index: Integer]: TJclMapLineNumber read GetLineNumberByIndex;
  258. end;
  259. type
  260. PJclDbgHeader = ^TJclDbgHeader;
  261. TJclDbgHeader = packed record
  262. Signature: DWORD;
  263. Version: Byte;
  264. Units: Integer;
  265. SourceNames: Integer;
  266. Symbols: Integer;
  267. LineNumbers: Integer;
  268. Words: Integer;
  269. ModuleName: Integer;
  270. CheckSum: Integer;
  271. CheckSumValid: Boolean;
  272. end;
  273. TJclBinDebugGenerator = class(TJclMapScanner)
  274. private
  275. FDataStream: TMemoryStream;
  276. FMapFileName: TFileName;
  277. protected
  278. procedure CreateData;
  279. public
  280. constructor Create(const MapFileName: TFileName; Module: HMODULE); override;
  281. destructor Destroy; override;
  282. function CalculateCheckSum: Boolean;
  283. property DataStream: TMemoryStream read FDataStream;
  284. end;
  285. TJclBinDbgNameCache = record
  286. Addr: DWORD;
  287. FirstWord: Integer;
  288. SecondWord: Integer;
  289. Text: string;
  290. end;
  291. TJclBinDebugScanner = class(TObject)
  292. private
  293. FCacheData: Boolean;
  294. FCacheProcNames: Boolean;
  295. FStream: TCustomMemoryStream;
  296. FValidFormat: Boolean;
  297. FLineNumbers: array of TJclMapLineNumber;
  298. FProcNames: array of TJclBinDbgNameCache;
  299. function GetModuleName: string;
  300. protected
  301. procedure CacheLineNumbers;
  302. procedure CacheProcNames;
  303. procedure CheckFormat;
  304. function DataToStr(A: Integer): string;
  305. function MakePtr(A: Integer): Pointer;
  306. class function ReadValue(var P: Pointer; var Value: Integer): Boolean; {$IFDEF SUPPORTS_STATIC}static;{$ENDIF}
  307. public
  308. constructor Create(AStream: TCustomMemoryStream; CacheData, CacheProcNames: Boolean);
  309. function IsModuleNameValid(const Name: TFileName): Boolean;
  310. function LineNumberFromAddr(Addr: DWORD): Integer; overload;
  311. function LineNumberFromAddr(Addr: DWORD; out Offset: Integer): Integer; overload;
  312. function ProcNameFromAddr(Addr: DWORD): string; overload;
  313. function ProcNameFromAddr(Addr: DWORD; out Offset: Integer): string; overload;
  314. function ModuleNameFromAddr(Addr: DWORD): string;
  315. function ModuleStartFromAddr(Addr: DWORD): DWORD;
  316. function SourceNameFromAddr(Addr: DWORD): string;
  317. property ModuleName: string read GetModuleName;
  318. property ValidFormat: Boolean read FValidFormat;
  319. function VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;
  320. end;
  321. function ConvertMapFileToJdbgFile(const MapFileName: TFileName): Boolean; overload;
  322. function ConvertMapFileToJdbgFile(const MapFileName: TFileName; out LinkerBugUnit: string;
  323. out LineNumberErrors: Integer): Boolean; overload;
  324. function ConvertMapFileToJdbgFile(const MapFileName: TFileName; out LinkerBugUnit: string;
  325. out LineNumberErrors, MapFileSize, JdbgFileSize: Integer): Boolean; overload;
  326. function InsertDebugDataIntoExecutableFile(const ExecutableFileName,
  327. MapFileName: TFileName; out LinkerBugUnit: string;
  328. out MapFileSize, JclDebugDataSize: Integer): Boolean; overload;
  329. function InsertDebugDataIntoExecutableFile(const ExecutableFileName,
  330. MapFileName: TFileName; out LinkerBugUnit: string;
  331. out MapFileSize, JclDebugDataSize, LineNumberErrors: Integer): Boolean; overload;
  332. function InsertDebugDataIntoExecutableFile(const ExecutableFileName: TFileName;
  333. BinDebug: TJclBinDebugGenerator; out LinkerBugUnit: string;
  334. out MapFileSize, JclDebugDataSize: Integer): Boolean; overload;
  335. function InsertDebugDataIntoExecutableFile(const ExecutableFileName: TFileName;
  336. BinDebug: TJclBinDebugGenerator; out LinkerBugUnit: string;
  337. out MapFileSize, JclDebugDataSize, LineNumberErrors: Integer): Boolean; overload;
  338. // Source Locations
  339. type
  340. TJclDebugInfoSource = class;
  341. PJclLocationInfo = ^TJclLocationInfo;
  342. TJclLocationInfo = record
  343. Address: Pointer; // Error address
  344. UnitName: string; // Name of Delphi unit
  345. ProcedureName: string; // Procedure name
  346. OffsetFromProcName: Integer; // Offset from Address to ProcedureName symbol location
  347. LineNumber: Integer; // Line number
  348. OffsetFromLineNumber: Integer; // Offset from Address to LineNumber symbol location
  349. SourceName: string; // Module file name
  350. DebugInfo: TJclDebugInfoSource; // Location object
  351. BinaryFileName: string; // Name of the binary file containing the symbol
  352. end;
  353. TJclLocationInfoExValues = set of (lievLocationInfo, lievProcedureStartLocationInfo, lievUnitVersionInfo);
  354. TJclCustomLocationInfoList = class;
  355. TJclLocationInfoListOptions = set of (liloAutoGetAddressInfo, liloAutoGetLocationInfo, liloAutoGetUnitVersionInfo);
  356. TJclLocationInfoEx = class(TPersistent)
  357. private
  358. FAddress: Pointer;
  359. FBinaryFileName: string;
  360. FDebugInfo: TJclDebugInfoSource;
  361. FLineNumber: Integer;
  362. FLineNumberOffsetFromProcedureStart: Integer;
  363. FModuleName: string;
  364. FOffsetFromLineNumber: Integer;
  365. FOffsetFromProcName: Integer;
  366. FParent: TJclCustomLocationInfoList;
  367. FProcedureName: string;
  368. FSourceName: string;
  369. FSourceUnitName: string;
  370. FUnitVersionDateTime: TDateTime;
  371. FUnitVersionExtra: string;
  372. FUnitVersionLogPath: string;
  373. FUnitVersionRCSfile: string;
  374. FUnitVersionRevision: string;
  375. FVAddress: Pointer;
  376. FValues: TJclLocationInfoExValues;
  377. procedure Fill(AOptions: TJclLocationInfoListOptions);
  378. function GetAsString: string;
  379. protected
  380. procedure AssignTo(Dest: TPersistent); override;
  381. public
  382. constructor Create(AParent: TJclCustomLocationInfoList; Address: Pointer);
  383. procedure Clear; virtual;
  384. property Address: Pointer read FAddress write FAddress;
  385. property AsString: string read GetAsString;
  386. property BinaryFileName: string read FBinaryFileName write FBinaryFileName;
  387. property DebugInfo: TJclDebugInfoSource read FDebugInfo write FDebugInfo;
  388. property LineNumber: Integer read FLineNumber write FLineNumber;
  389. property LineNumberOffsetFromProcedureStart: Integer read FLineNumberOffsetFromProcedureStart write FLineNumberOffsetFromProcedureStart;
  390. property ModuleName: string read FModuleName write FModuleName;
  391. property OffsetFromLineNumber: Integer read FOffsetFromLineNumber write FOffsetFromLineNumber;
  392. property OffsetFromProcName: Integer read FOffsetFromProcName write FOffsetFromProcName;
  393. property ProcedureName: string read FProcedureName write FProcedureName;
  394. property SourceName: string read FSourceName write FSourceName;
  395. { this is equal to TJclLocationInfo.UnitName, but has been renamed because
  396. UnitName is a class function in TObject since Delphi 2009 }
  397. property SourceUnitName: string read FSourceUnitName write FSourceUnitName;
  398. property UnitVersionDateTime: TDateTime read FUnitVersionDateTime write FUnitVersionDateTime;
  399. property UnitVersionExtra: string read FUnitVersionExtra write FUnitVersionExtra;
  400. property UnitVersionLogPath: string read FUnitVersionLogPath write FUnitVersionLogPath;
  401. property UnitVersionRCSfile: string read FUnitVersionRCSfile write FUnitVersionRCSfile;
  402. property UnitVersionRevision: string read FUnitVersionRevision write FUnitVersionRevision;
  403. property VAddress: Pointer read FVAddress write FVAddress;
  404. property Values: TJclLocationInfoExValues read FValues write FValues;
  405. end;
  406. TJclLocationInfoClass = class of TJclLocationInfoEx;
  407. TJclCustomLocationInfoListClass = class of TJclCustomLocationInfoList;
  408. TJclCustomLocationInfoList = class(TPersistent)
  409. protected
  410. FItemClass: TJclLocationInfoClass;
  411. FItems: TObjectList;
  412. FOptions: TJclLocationInfoListOptions;
  413. function GetAsString: string;
  414. function GetCount: Integer;
  415. function InternalAdd(Addr: Pointer): TJclLocationInfoEx;
  416. protected
  417. procedure AssignTo(Dest: TPersistent); override;
  418. public
  419. constructor Create; virtual;
  420. destructor Destroy; override;
  421. procedure AddStackInfoList(AStackInfoList: TObject);
  422. procedure Clear;
  423. property AsString: string read GetAsString;
  424. property Count: Integer read GetCount;
  425. property Options: TJclLocationInfoListOptions read FOptions write FOptions;
  426. end;
  427. TJclLocationInfoList = class(TJclCustomLocationInfoList)
  428. private
  429. function GetItems(AIndex: Integer): TJclLocationInfoEx;
  430. public
  431. constructor Create; override;
  432. function Add(Addr: Pointer): TJclLocationInfoEx;
  433. property Items[AIndex: Integer]: TJclLocationInfoEx read GetItems; default;
  434. end;
  435. TJclDebugInfoSource = class(TObject)
  436. private
  437. FModule: HMODULE;
  438. FModuleCodeSize: SizeInt;
  439. function GetFileName: TFileName;
  440. protected
  441. function VAFromAddr(const Addr: Pointer): DWORD; virtual;
  442. function AddrFromVA(const VA: DWORD): Pointer; virtual;
  443. public
  444. constructor Create(AModule: HMODULE); virtual;
  445. function InitializeSource: Boolean; virtual; abstract;
  446. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; virtual; abstract;
  447. function GetAddress(const UnitName, ProcName: string): Pointer; virtual; abstract;
  448. property Module: HMODULE read FModule;
  449. property FileName: TFileName read GetFileName;
  450. property ModuleCodeSize: SizeInt read FModuleCodeSize;
  451. end;
  452. TJclDebugInfoSourceClass = class of TJclDebugInfoSource;
  453. TJclDebugInfoList = class(TObjectList)
  454. private
  455. function GetItemFromModule(const Module: HMODULE): TJclDebugInfoSource;
  456. function GetItems(Index: Integer): TJclDebugInfoSource;
  457. protected
  458. function CreateDebugInfo(const Module: HMODULE): TJclDebugInfoSource;
  459. public
  460. class procedure RegisterDebugInfoSource(
  461. const InfoSourceClass: TJclDebugInfoSourceClass);
  462. class procedure UnRegisterDebugInfoSource(
  463. const InfoSourceClass: TJclDebugInfoSourceClass);
  464. class procedure RegisterDebugInfoSourceFirst(
  465. const InfoSourceClass: TJclDebugInfoSourceClass);
  466. class procedure NeedInfoSourceClassList;
  467. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;
  468. property ItemFromModule[const Module: HMODULE]: TJclDebugInfoSource read GetItemFromModule;
  469. property Items[Index: Integer]: TJclDebugInfoSource read GetItems;
  470. end;
  471. // Various source location implementations
  472. TJclDebugInfoMap = class(TJclDebugInfoSource)
  473. private
  474. FScanner: TJclMapScanner;
  475. public
  476. destructor Destroy; override;
  477. function InitializeSource: Boolean; override;
  478. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;
  479. function GetAddress(const UnitName, ProcName: string): Pointer; override;
  480. end;
  481. TJclDebugInfoBinary = class(TJclDebugInfoSource)
  482. private
  483. FScanner: TJclBinDebugScanner;
  484. FStream: TCustomMemoryStream;
  485. public
  486. destructor Destroy; override;
  487. function InitializeSource: Boolean; override;
  488. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;
  489. function GetAddress(const UnitName, ProcName: string): Pointer; override;
  490. end;
  491. TJclDebugInfoExports = class(TJclDebugInfoSource)
  492. private
  493. {$IFDEF BORLAND}
  494. FImage: TJclPeBorImage;
  495. {$ENDIF BORLAND}
  496. {$IFDEF FPC}
  497. FImage: TJclPeImage;
  498. {$ENDIF FPC}
  499. function IsAddressInThisExportedFunction(Addr: PByteArray; FunctionStartAddr: TJclAddr): Boolean;
  500. public
  501. destructor Destroy; override;
  502. function InitializeSource: Boolean; override;
  503. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;
  504. function GetAddress(const UnitName, ProcName: string): Pointer; override;
  505. end;
  506. {$IFDEF BORLAND}
  507. TJclDebugInfoTD32 = class(TJclDebugInfoSource)
  508. private
  509. FImage: TJclPeBorTD32Image;
  510. public
  511. destructor Destroy; override;
  512. function InitializeSource: Boolean; override;
  513. procedure GenerateUnmangledNames;
  514. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;
  515. function GetAddress(const UnitName, ProcName: string): Pointer; override;
  516. end;
  517. {$ENDIF BORLAND}
  518. TJclDebugInfoSymbols = class(TJclDebugInfoSource)
  519. public
  520. class function LoadDebugFunctions: Boolean;
  521. class function UnloadDebugFunctions: Boolean;
  522. class function InitializeDebugSymbols: Boolean;
  523. class function CleanupDebugSymbols: Boolean;
  524. function InitializeSource: Boolean; override;
  525. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;
  526. function GetAddress(const UnitName, ProcName: string): Pointer; override;
  527. end;
  528. // Source location functions
  529. function Caller(Level: Integer = 0; FastStackWalk: Boolean = False): Pointer;
  530. procedure BeginGetLocationInfoCache;
  531. procedure EndGetLocationInfoCache;
  532. function GetLocationInfo(const Addr: Pointer): TJclLocationInfo; overload;
  533. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; overload;
  534. function GetLocationInfoStr(const Addr: Pointer; IncludeModuleName: Boolean = False;
  535. IncludeAddressOffset: Boolean = False; IncludeStartProcLineOffset: Boolean = False;
  536. IncludeVAddress: Boolean = False): string;
  537. function DebugInfoAvailable(const Module: HMODULE): Boolean;
  538. procedure ClearLocationData;
  539. function FileByLevel(const Level: Integer = 0): string;
  540. function ModuleByLevel(const Level: Integer = 0): string;
  541. function ProcByLevel(const Level: Integer = 0; OnlyProcedureName: boolean =false): string;
  542. function LineByLevel(const Level: Integer = 0): Integer;
  543. function MapByLevel(const Level: Integer; var File_, Module_, Proc_: string; var Line_: Integer): Boolean;
  544. function FileOfAddr(const Addr: Pointer): string;
  545. function ModuleOfAddr(const Addr: Pointer): string;
  546. function ProcOfAddr(const Addr: Pointer): string;
  547. function LineOfAddr(const Addr: Pointer): Integer;
  548. function MapOfAddr(const Addr: Pointer; var File_, Module_, Proc_: string; var Line_: Integer): Boolean;
  549. function ExtractClassName(const ProcedureName: string): string;
  550. function ExtractMethodName(const ProcedureName: string): string;
  551. // Original function names, deprecated will be removed in V2.0; do not use!
  552. function __FILE__(const Level: Integer = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  553. function __MODULE__(const Level: Integer = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  554. function __PROC__(const Level: Integer = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  555. function __LINE__(const Level: Integer = 0): Integer; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  556. function __MAP__(const Level: Integer; var _File, _Module, _Proc: string; var _Line: Integer): Boolean; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  557. function __FILE_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  558. function __MODULE_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  559. function __PROC_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  560. function __LINE_OF_ADDR__(const Addr: Pointer): Integer; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  561. function __MAP_OF_ADDR__(const Addr: Pointer; var _File, _Module, _Proc: string;
  562. var _Line: Integer): Boolean; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}
  563. // Stack info routines base list
  564. type
  565. TJclStackBaseList = class(TObjectList)
  566. private
  567. FThreadID: DWORD;
  568. FTimeStamp: TDateTime;
  569. protected
  570. FOnDestroy: TNotifyEvent;
  571. public
  572. constructor Create;
  573. destructor Destroy; override;
  574. property ThreadID: DWORD read FThreadID;
  575. property TimeStamp: TDateTime read FTimeStamp;
  576. end;
  577. // Stack info routines
  578. type
  579. PDWORD_PTRArray = ^TDWORD_PTRArray;
  580. TDWORD_PTRArray = array [0..(MaxInt - $F) div SizeOf(DWORD_PTR)] of DWORD_PTR;
  581. {$IFNDEF FPC}
  582. PDWORD_PTR = ^DWORD_PTR;
  583. {$ENDIF ~FPC}
  584. PStackFrame = ^TStackFrame;
  585. TStackFrame = record
  586. CallerFrame: TJclAddr;
  587. CallerAddr: TJclAddr;
  588. end;
  589. PStackInfo = ^TStackInfo;
  590. TStackInfo = record
  591. CallerAddr: TJclAddr;
  592. Level: Integer;
  593. CallerFrame: TJclAddr;
  594. DumpSize: DWORD;
  595. ParamSize: DWORD;
  596. ParamPtr: PDWORD_PTRArray;
  597. case Integer of
  598. 0:
  599. (StackFrame: PStackFrame);
  600. 1:
  601. (DumpPtr: PJclByteArray);
  602. end;
  603. TJclStackInfoItem = class(TObject)
  604. private
  605. FStackInfo: TStackInfo;
  606. function GetCallerAddr: Pointer;
  607. function GetLogicalAddress: TJclAddr;
  608. public
  609. property CallerAddr: Pointer read GetCallerAddr;
  610. property LogicalAddress: TJclAddr read GetLogicalAddress;
  611. property StackInfo: TStackInfo read FStackInfo;
  612. end;
  613. TJclStackInfoList = class(TJclStackBaseList)
  614. private
  615. FIgnoreLevels: Integer;
  616. TopOfStack: TJclAddr;
  617. BaseOfStack: TJclAddr;
  618. FStackData: PPointer;
  619. FFramePointer: Pointer;
  620. FModuleInfoList: TJclModuleInfoList;
  621. FCorrectOnAccess: Boolean;
  622. FSkipFirstItem: Boolean;
  623. FDelayedTrace: Boolean;
  624. FInStackTracing: Boolean;
  625. FRaw: Boolean;
  626. FStackOffset: Int64;
  627. {$IFDEF CPU64}
  628. procedure CaptureBackTrace;
  629. {$ENDIF CPU64}
  630. function GetItems(Index: Integer): TJclStackInfoItem;
  631. function NextStackFrame(var StackFrame: PStackFrame; var StackInfo: TStackInfo): Boolean;
  632. procedure StoreToList(const StackInfo: TStackInfo);
  633. procedure TraceStackFrames;
  634. procedure TraceStackRaw;
  635. {$IFDEF CPU32}
  636. procedure DelayStoreStack;
  637. {$ENDIF CPU32}
  638. function ValidCallSite(CodeAddr: TJclAddr; out CallInstructionSize: Cardinal): Boolean;
  639. function ValidStackAddr(StackAddr: TJclAddr): Boolean;
  640. function GetCount: Integer;
  641. procedure CorrectOnAccess(ASkipFirstItem: Boolean);
  642. public
  643. constructor Create(ARaw: Boolean; AIgnoreLevels: Integer;
  644. AFirstCaller: Pointer); overload;
  645. constructor Create(ARaw: Boolean; AIgnoreLevels: Integer;
  646. AFirstCaller: Pointer; ADelayedTrace: Boolean); overload;
  647. constructor Create(ARaw: Boolean; AIgnoreLevels: Integer;
  648. AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack: Pointer); overload;
  649. constructor Create(ARaw: Boolean; AIgnoreLevels: Integer;
  650. AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack, ATopOfStack: Pointer); overload;
  651. destructor Destroy; override;
  652. procedure ForceStackTracing;
  653. procedure AddToStrings(Strings: TStrings; IncludeModuleName: Boolean = False;
  654. IncludeAddressOffset: Boolean = False; IncludeStartProcLineOffset: Boolean = False;
  655. IncludeVAddress: Boolean = False);
  656. property DelayedTrace: Boolean read FDelayedTrace;
  657. property Items[Index: Integer]: TJclStackInfoItem read GetItems; default;
  658. property IgnoreLevels: Integer read FIgnoreLevels;
  659. property Count: Integer read GetCount;
  660. property Raw: Boolean read FRaw;
  661. end;
  662. function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer): TJclStackInfoList; overload;
  663. function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;
  664. DelayedTrace: Boolean): TJclStackInfoList; overload;
  665. function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;
  666. DelayedTrace: Boolean; BaseOfStack: Pointer): TJclStackInfoList; overload;
  667. function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;
  668. DelayedTrace: Boolean; BaseOfStack, TopOfStack: Pointer): TJclStackInfoList; overload;
  669. function JclCreateThreadStackTrace(Raw: Boolean; const ThreadHandle: THandle): TJclStackInfoList;
  670. function JclCreateThreadStackTraceFromID(Raw: Boolean; ThreadID: DWORD): TJclStackInfoList;
  671. function JclLastExceptStackList: TJclStackInfoList;
  672. function JclLastExceptStackListToStrings(Strings: TStrings; IncludeModuleName: Boolean = False;
  673. IncludeAddressOffset: Boolean = False; IncludeStartProcLineOffset: Boolean = False;
  674. IncludeVAddress: Boolean = False): Boolean;
  675. function JclGetExceptStackList(ThreadID: DWORD): TJclStackInfoList;
  676. function JclGetExceptStackListToStrings(ThreadID: DWORD; Strings: TStrings;
  677. IncludeModuleName: Boolean = False; IncludeAddressOffset: Boolean = False;
  678. IncludeStartProcLineOffset: Boolean = False; IncludeVAddress: Boolean = False): Boolean;
  679. // helper function for DUnit runtime memory leak check
  680. procedure JclClearGlobalStackData;
  681. // Exception frame info routines
  682. type
  683. PJmpInstruction = ^TJmpInstruction;
  684. TJmpInstruction = packed record // from System.pas
  685. OpCode: Byte;
  686. Distance: Longint;
  687. end;
  688. TExcDescEntry = record // from System.pas
  689. VTable: Pointer;
  690. Handler: Pointer;
  691. end;
  692. PExcDesc = ^TExcDesc;
  693. TExcDesc = packed record // from System.pas
  694. JMP: TJmpInstruction;
  695. case Integer of
  696. 0:
  697. (Instructions: array [0..0] of Byte);
  698. 1:
  699. (Cnt: Integer;
  700. ExcTab: array [0..0] of TExcDescEntry);
  701. end;
  702. PExcFrame = ^TExcFrame;
  703. TExcFrame = record // from System.pas
  704. Next: PExcFrame;
  705. Desc: PExcDesc;
  706. FramePointer: Pointer;
  707. case Integer of
  708. 0:
  709. ();
  710. 1:
  711. (ConstructedObject: Pointer);
  712. 2:
  713. (SelfOfMethod: Pointer);
  714. end;
  715. PJmpTable = ^TJmpTable;
  716. TJmpTable = packed record
  717. OPCode: Word; // FF 25 = JMP DWORD PTR [$xxxxxxxx], encoded as $25FF
  718. Ptr: Pointer;
  719. end;
  720. TExceptFrameKind =
  721. (efkUnknown, efkFinally, efkAnyException, efkOnException, efkAutoException);
  722. TJclExceptFrame = class(TObject)
  723. private
  724. FFrameKind: TExceptFrameKind;
  725. FFrameLocation: Pointer;
  726. FCodeLocation: Pointer;
  727. FExcTab: array of TExcDescEntry;
  728. protected
  729. procedure AnalyseExceptFrame(AExcDesc: PExcDesc);
  730. public
  731. constructor Create(AFrameLocation: Pointer; AExcDesc: PExcDesc);
  732. function Handles(ExceptObj: TObject): Boolean;
  733. function HandlerInfo(ExceptObj: TObject; out HandlerAt: Pointer): Boolean;
  734. property CodeLocation: Pointer read FCodeLocation;
  735. property FrameLocation: Pointer read FFrameLocation;
  736. property FrameKind: TExceptFrameKind read FFrameKind;
  737. end;
  738. TJclExceptFrameList = class(TJclStackBaseList)
  739. private
  740. FIgnoreLevels: Integer;
  741. function GetItems(Index: Integer): TJclExceptFrame;
  742. protected
  743. function AddFrame(AFrame: PExcFrame): TJclExceptFrame;
  744. public
  745. constructor Create(AIgnoreLevels: Integer);
  746. procedure TraceExceptionFrames;
  747. property Items[Index: Integer]: TJclExceptFrame read GetItems;
  748. property IgnoreLevels: Integer read FIgnoreLevels write FIgnoreLevels;
  749. end;
  750. function JclCreateExceptFrameList(AIgnoreLevels: Integer): TJclExceptFrameList;
  751. function JclLastExceptFrameList: TJclExceptFrameList;
  752. function JclGetExceptFrameList(ThreadID: DWORD): TJclExceptFrameList;
  753. function JclStartExceptionTracking: Boolean;
  754. function JclStopExceptionTracking: Boolean;
  755. function JclExceptionTrackingActive: Boolean;
  756. function JclTrackExceptionsFromLibraries: Boolean;
  757. // Thread exception tracking support
  758. type
  759. TJclDebugThread = class(TThread)
  760. private
  761. FSyncException: TObject;
  762. FThreadName: string;
  763. procedure DoHandleException;
  764. function GetThreadInfo: string;
  765. protected
  766. procedure DoNotify;
  767. procedure DoSyncHandleException; dynamic;
  768. procedure HandleException(Sender: TObject = nil);
  769. public
  770. constructor Create(ASuspended: Boolean; const AThreadName: string = '');
  771. destructor Destroy; override;
  772. property SyncException: TObject read FSyncException;
  773. property ThreadInfo: string read GetThreadInfo;
  774. property ThreadName: string read FThreadName;
  775. end;
  776. TJclDebugThreadNotifyEvent = procedure(Thread: TJclDebugThread) of object;
  777. TJclThreadIDNotifyEvent = procedure(ThreadID: DWORD) of object;
  778. TJclDebugThreadList = class(TObject)
  779. private
  780. FList: TObjectList;
  781. FLock: TJclCriticalSection;
  782. FReadLock: TJclCriticalSection;
  783. FRegSyncThreadID: DWORD;
  784. FSaveCreationStack: Boolean;
  785. FUnregSyncThreadID: DWORD;
  786. FOnSyncException: TJclDebugThreadNotifyEvent;
  787. FOnThreadRegistered: TJclThreadIDNotifyEvent;
  788. FOnThreadUnregistered: TJclThreadIDNotifyEvent;
  789. function GetThreadClassNames(ThreadID: DWORD): string;
  790. function GetThreadInfos(ThreadID: DWORD): string;
  791. function GetThreadNames(ThreadID: DWORD): string;
  792. procedure DoSyncThreadRegistered;
  793. procedure DoSyncThreadUnregistered;
  794. function GetThreadCreationTime(ThreadID: DWORD): TDateTime;
  795. function GetThreadHandle(Index: Integer): THandle;
  796. function GetThreadID(Index: Integer): DWORD;
  797. function GetThreadIDCount: Integer;
  798. function GetThreadParentID(ThreadID: DWORD): DWORD;
  799. function GetThreadValues(ThreadID: DWORD; Index: Integer): string;
  800. function IndexOfThreadID(ThreadID: DWORD): Integer;
  801. protected
  802. procedure DoSyncException(Thread: TJclDebugThread);
  803. procedure DoThreadRegistered(Thread: TThread);
  804. procedure DoThreadUnregistered(Thread: TThread);
  805. procedure InternalRegisterThread(Thread: TThread; ThreadID: DWORD; const ThreadName: string);
  806. procedure InternalUnregisterThread(Thread: TThread; ThreadID: DWORD);
  807. public
  808. constructor Create;
  809. destructor Destroy; override;
  810. function AddStackListToLocationInfoList(ThreadID: DWORD; AList: TJclLocationInfoList): Boolean;
  811. procedure RegisterThread(Thread: TThread; const ThreadName: string);
  812. procedure RegisterThreadID(AThreadID: DWORD; const ThreadName: string = '');
  813. procedure UnregisterThread(Thread: TThread);
  814. procedure UnregisterThreadID(AThreadID: DWORD);
  815. property Lock: TJclCriticalSection read FLock;
  816. //property ThreadClassNames[ThreadID: DWORD]: string index 1 read GetThreadValues;
  817. property SaveCreationStack: Boolean read FSaveCreationStack write FSaveCreationStack;
  818. property ThreadClassNames[ThreadID: DWORD]: string read GetThreadClassNames;
  819. property ThreadCreationTime[ThreadID: DWORD]: TDateTime read GetThreadCreationTime;
  820. property ThreadHandles[Index: Integer]: THandle read GetThreadHandle;
  821. property ThreadIDs[Index: Integer]: DWORD read GetThreadID;
  822. property ThreadIDCount: Integer read GetThreadIDCount;
  823. //property ThreadInfos[ThreadID: DWORD]: string index 2 read GetThreadValues;
  824. property ThreadInfos[ThreadID: DWORD]: string read GetThreadInfos;
  825. //property ThreadNames[ThreadID: DWORD]: string index 0 read GetThreadValues;
  826. property ThreadNames[ThreadID: DWORD]: string read GetThreadNames;
  827. property ThreadParentIDs[ThreadID: DWORD]: DWORD read GetThreadParentID;
  828. property OnSyncException: TJclDebugThreadNotifyEvent read FOnSyncException write FOnSyncException;
  829. property OnThreadRegistered: TJclThreadIDNotifyEvent read FOnThreadRegistered write FOnThreadRegistered;
  830. property OnThreadUnregistered: TJclThreadIDNotifyEvent read FOnThreadUnregistered write FOnThreadUnregistered;
  831. end;
  832. TJclDebugThreadInfo = class(TObject)
  833. private
  834. FCreationTime: TDateTime;
  835. FParentThreadID: DWORD;
  836. FStackList: TJclStackInfoList;
  837. FThreadClassName: string;
  838. FThreadID: DWORD;
  839. FThreadHandle: THandle;
  840. FThreadName: string;
  841. public
  842. constructor Create(AParentThreadID, AThreadID: DWORD; AStack: Boolean);
  843. destructor Destroy; override;
  844. property CreationTime: TDateTime read FCreationTime;
  845. property ParentThreadID: DWORD read FParentThreadID;
  846. property StackList: TJclStackInfoList read FStackList;
  847. property ThreadClassName: string read FThreadClassName write FThreadClassName;
  848. property ThreadID: DWORD read FThreadID;
  849. property ThreadHandle: THandle read FThreadHandle write FThreadHandle;
  850. property ThreadName: string read FThreadName write FThreadName;
  851. end;
  852. TJclThreadInfoOptions = set of (tioIsMainThread, tioName, tioCreationTime, tioParentThreadID, tioStack, tioCreationStack);
  853. TJclCustomThreadInfo = class(TPersistent)
  854. protected
  855. FCreationTime: TDateTime;
  856. FCreationStack: TJclCustomLocationInfoList;
  857. FName: string;
  858. FParentThreadID: DWORD;
  859. FStack: TJclCustomLocationInfoList;
  860. FThreadID: DWORD;
  861. FValues: TJclThreadInfoOptions;
  862. procedure AssignTo(Dest: TPersistent); override;
  863. function GetStackClass: TJclCustomLocationInfoListClass; virtual;
  864. public
  865. constructor Create;
  866. destructor Destroy; override;
  867. property CreationTime: TDateTime read FCreationTime write FCreationTime;
  868. property Name: string read FName write FName;
  869. property ParentThreadID: DWORD read FParentThreadID write FParentThreadID;
  870. property ThreadID: DWORD read FThreadID write FThreadID;
  871. property Values: TJclThreadInfoOptions read FValues write FValues;
  872. end;
  873. TJclThreadInfo = class(TJclCustomThreadInfo)
  874. private
  875. function GetAsString: string;
  876. procedure InternalFill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions; AExceptThread: Boolean);
  877. function GetStack(const AIndex: Integer): TJclLocationInfoList;
  878. protected
  879. function GetStackClass: TJclCustomLocationInfoListClass; override;
  880. public
  881. procedure Fill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions);
  882. procedure FillFromExceptThread(AGatherOptions: TJclThreadInfoOptions);
  883. property AsString: string read GetAsString;
  884. property CreationStack: TJclLocationInfoList index 1 read GetStack;
  885. property Stack: TJclLocationInfoList index 2 read GetStack;
  886. end;
  887. TJclThreadInfoList = class(TPersistent)
  888. private
  889. FGatherOptions: TJclThreadInfoOptions;
  890. FItems: TObjectList;
  891. function GetAsString: string;
  892. function GetCount: Integer;
  893. function GetItems(AIndex: Integer): TJclThreadInfo;
  894. procedure InternalGather(AIncludeThreadIDs, AExcludeThreadIDs: array of DWORD);
  895. protected
  896. procedure AssignTo(Dest: TPersistent); override;
  897. public
  898. constructor Create;
  899. destructor Destroy; override;
  900. function Add: TJclThreadInfo;
  901. procedure Clear;
  902. procedure Gather(AExceptThreadID: DWORD);
  903. procedure GatherExclude(AThreadIDs: array of DWORD);
  904. procedure GatherInclude(AThreadIDs: array of DWORD);
  905. property AsString: string read GetAsString;
  906. property Count: Integer read GetCount;
  907. property GatherOptions: TJclThreadInfoOptions read FGatherOptions write FGatherOptions;
  908. property Items[AIndex: Integer]: TJclThreadInfo read GetItems; default;
  909. end;
  910. function JclDebugThreadList: TJclDebugThreadList;
  911. function JclHookThreads: Boolean;
  912. function JclUnhookThreads: Boolean;
  913. function JclThreadsHooked: Boolean;
  914. // Miscellanuous
  915. {$IFDEF MSWINDOWS}
  916. function EnableCrashOnCtrlScroll(const Enable: Boolean): Boolean;
  917. function IsDebuggerAttached: Boolean;
  918. function IsHandleValid(Handle: THandle): Boolean;
  919. {$ENDIF MSWINDOWS}
  920. {$IFDEF SUPPORTS_EXTSYM}
  921. {$EXTERNALSYM __FILE__}
  922. {$EXTERNALSYM __LINE__}
  923. {$ENDIF SUPPORTS_EXTSYM}
  924. const
  925. EnvironmentVarNtSymbolPath = '_NT_SYMBOL_PATH'; // do not localize
  926. EnvironmentVarAlternateNtSymbolPath = '_NT_ALTERNATE_SYMBOL_PATH'; // do not localize
  927. MaxStackTraceItems = 4096;
  928. // JCL binary debug data generator and scanner
  929. const
  930. JclDbgDataSignature = $4742444A; // JDBG
  931. JclDbgDataResName = AnsiString('JCLDEBUG'); // do not localize
  932. JclDbgHeaderVersion = 1; // JCL 1.11 and 1.20
  933. JclDbgFileExtension = '.jdbg'; // do not localize
  934. JclMapFileExtension = '.map'; // do not localize
  935. DrcFileExtension = '.drc'; // do not localize
  936. // Global exceptional stack tracker enable routines and variables
  937. type
  938. TJclStackTrackingOption =
  939. (stStack, stExceptFrame, stRawMode, stAllModules, stStaticModuleList,
  940. stDelayedTrace, stTraceAllExceptions, stMainThreadOnly, stDisableIfDebuggerAttached
  941. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  942. // Resolves the Exception.Stacktrace string when the exception is raised. This is more
  943. // exact if modules are unloaded before the delayed resolving happens, but it slows down
  944. // the exception handling if no stacktrace is needed for the exception.
  945. , stImmediateExceptionStacktraceResolving
  946. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  947. // stCleanRawStack does a deeper analysis of the callstack by evaluating the instructions
  948. // that manipulate the stack.
  949. // It removes many cases of false positives but may also remove valid entries if it runs
  950. // into a function that does non-standard stack pointer manipulation.
  951. , stCleanRawStack // experimental
  952. );
  953. TJclStackTrackingOptions = set of TJclStackTrackingOption;
  954. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  955. TJclExceptionStacktraceOption = (
  956. estoIncludeModuleName,
  957. estoIncludeAdressOffset,
  958. estoIncludeStartProcLineOffset,
  959. estoIncludeVAddress
  960. );
  961. TJclExceptionStacktraceOptions = set of TJclExceptionStacktraceOption;
  962. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  963. var
  964. JclStackTrackingOptions: TJclStackTrackingOptions = [stStack];
  965. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  966. // JclExceptionStacktraceOptions controls the Exception.Stacktrace string's format
  967. JclExceptionStacktraceOptions: TJclExceptionStacktraceOptions =
  968. [estoIncludeModuleName, estoIncludeAdressOffset, estoIncludeStartProcLineOffset, estoIncludeVAddress];
  969. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  970. { JclDebugInfoSymbolPaths specifies a list of paths, separated by ';', in
  971. which the DebugInfoSymbol scanner should look for symbol information. }
  972. JclDebugInfoSymbolPaths: string = '';
  973. // functions to add/remove exception classes to be ignored if StTraceAllExceptions is not set
  974. procedure AddIgnoredException(const ExceptionClass: TClass);
  975. procedure AddIgnoredExceptionByName(const AExceptionClassName: string);
  976. procedure RemoveIgnoredException(const ExceptionClass: TClass);
  977. procedure RemoveIgnoredExceptionByName(const AExceptionClassName: string);
  978. function IsIgnoredException(const ExceptionClass: TClass): Boolean;
  979. // function to add additional system modules to be included in the stack trace
  980. procedure AddModule(const ModuleName: string);
  981. {$IFDEF UNITVERSIONING}
  982. const
  983. UnitVersioning: TUnitVersionInfo = (
  984. RCSfile: '$URL$';
  985. Revision: '$Revision$';
  986. Date: '$Date$';
  987. LogPath: 'JCL\source\windows';
  988. Extra: '';
  989. Data: nil
  990. );
  991. {$ENDIF UNITVERSIONING}
  992. implementation
  993. uses
  994. {$IFDEF HAS_UNITSCOPE}
  995. System.RTLConsts,
  996. System.Types, // for inlining TList.Remove
  997. {$IFDEF HAS_UNIT_CHARACTER}
  998. System.Character,
  999. {$ENDIF HAS_UNIT_CHARACTER}
  1000. {$IFDEF SUPPORTS_GENERICS}
  1001. System.Generics.Collections,
  1002. {$ENDIF SUPPORTS_GENERICS}
  1003. {$ELSE ~HAS_UNITSCOPE}
  1004. RTLConsts,
  1005. {$IFDEF HAS_UNIT_CHARACTER}
  1006. Character,
  1007. {$ENDIF HAS_UNIT_CHARACTER}
  1008. {$IFDEF SUPPORTS_GENERICS}
  1009. Generics.Collections,
  1010. {$ENDIF SUPPORTS_GENERICS}
  1011. {$ENDIF ~HAS_UNITSCOPE}
  1012. {$IFDEF MSWINDOWS}
  1013. JclRegistry,
  1014. {$ENDIF MSWINDOWS}
  1015. JclHookExcept, JclAnsiStrings, JclStrings, JclSysInfo, JclSysUtils, JclWin32,
  1016. JclStringConversions, JclResources;
  1017. //=== Helper assembler routines ==============================================
  1018. const
  1019. ModuleCodeOffset = $1000;
  1020. var
  1021. HexMap: array[AnsiChar] of Byte;
  1022. JclDebugFinalized: Boolean;
  1023. GlobalStackListLiveCount: Integer;
  1024. procedure FreeJclDebugGlobals;
  1025. forward;
  1026. {$STACKFRAMES OFF}
  1027. function GetFramePointer: Pointer;
  1028. asm
  1029. {$IFDEF CPU32}
  1030. MOV EAX, EBP
  1031. {$ENDIF CPU32}
  1032. {$IFDEF CPU64}
  1033. MOV RAX, RBP
  1034. {$ENDIF CPU64}
  1035. end;
  1036. function GetStackPointer: Pointer;
  1037. asm
  1038. {$IFDEF CPU32}
  1039. MOV EAX, ESP
  1040. {$ENDIF CPU32}
  1041. {$IFDEF CPU64}
  1042. MOV RAX, RSP
  1043. {$ENDIF CPU64}
  1044. end;
  1045. {$IFDEF CPU32}
  1046. function GetExceptionPointer: Pointer;
  1047. asm
  1048. XOR EAX, EAX
  1049. MOV EAX, FS:[EAX]
  1050. end;
  1051. {$ENDIF CPU32}
  1052. // Reference: Matt Pietrek, MSJ, Under the hood, on TIBs:
  1053. // http://www.microsoft.com/MSJ/archive/S2CE.HTM
  1054. function GetStackTop: TJclAddr;
  1055. asm
  1056. {$IFDEF CPU32}
  1057. MOV EAX, FS:[0].NT_TIB32.StackBase
  1058. {$ENDIF CPU32}
  1059. {$IFDEF CPU64}
  1060. {$IFDEF DELPHI64_TEMPORARY}
  1061. //TODO: check if the FS version doesn't work in general in 64-bit mode
  1062. MOV RAX, GS:[ABS 8]
  1063. {$ELSE ~DELPHI64_TEMPORARY}
  1064. MOV RAX, FS:[0].NT_TIB64.StackBase
  1065. {$ENDIF ~DELPHI64_TEMPORARY}
  1066. {$ENDIF CPU64}
  1067. end;
  1068. {$IFDEF STACKFRAMES_ON}
  1069. {$STACKFRAMES ON}
  1070. {$ENDIF STACKFRAMES_ON}
  1071. //=== Diagnostics ===========================================================
  1072. procedure AssertKindOf(const ClassName: string; const Obj: TObject);
  1073. var
  1074. C: TClass;
  1075. begin
  1076. if not Obj.ClassNameIs(ClassName) then
  1077. begin
  1078. C := Obj.ClassParent;
  1079. while (C <> nil) and (not C.ClassNameIs(ClassName)) do
  1080. C := C.ClassParent;
  1081. Assert(C <> nil);
  1082. end;
  1083. end;
  1084. procedure AssertKindOf(const ClassType: TClass; const Obj: TObject);
  1085. begin
  1086. Assert(Obj.InheritsFrom(ClassType));
  1087. end;
  1088. procedure TraceMsg(const Msg: string);
  1089. begin
  1090. OutputDebugString(PChar(StrDoubleQuote(Msg)));
  1091. end;
  1092. procedure TraceFmt(const Fmt: string; const Args: array of const);
  1093. begin
  1094. OutputDebugString(PChar(Format(StrDoubleQuote(Fmt), Args)));
  1095. end;
  1096. procedure TraceLoc(const Msg: string);
  1097. begin
  1098. OutputDebugString(PChar(Format('%s:%u (%s) "%s"',
  1099. [FileByLevel(1), LineByLevel(1), ProcByLevel(1), Msg])));
  1100. end;
  1101. procedure TraceLocFmt(const Fmt: string; const Args: array of const);
  1102. var
  1103. S: string;
  1104. begin
  1105. S := Format('%s:%u (%s) ', [FileByLevel(1), LineByLevel(1), ProcByLevel(1)]) +
  1106. Format(StrDoubleQuote(Fmt), Args);
  1107. OutputDebugString(PChar(S));
  1108. end;
  1109. //=== { TJclModuleInfoList } =================================================
  1110. constructor TJclModuleInfoList.Create(ADynamicBuild, ASystemModulesOnly: Boolean);
  1111. begin
  1112. inherited Create(True);
  1113. FDynamicBuild := ADynamicBuild;
  1114. FSystemModulesOnly := ASystemModulesOnly;
  1115. if not FDynamicBuild then
  1116. BuildModulesList;
  1117. end;
  1118. function TJclModuleInfoList.AddModule(Module: HMODULE; SystemModule: Boolean): Boolean;
  1119. begin
  1120. Result := not IsValidModuleAddress(Pointer(Module)) and
  1121. (CreateItemForAddress(Pointer(Module), SystemModule) <> nil);
  1122. end;
  1123. {function SortByStartAddress(Item1, Item2: Pointer): Integer;
  1124. begin
  1125. Result := INT_PTR(TJclModuleInfo(Item2).StartAddr) - INT_PTR(TJclModuleInfo(Item1).StartAddr);
  1126. end;}
  1127. procedure TJclModuleInfoList.BuildModulesList;
  1128. var
  1129. List: TStringList;
  1130. I: Integer;
  1131. CurModule: PLibModule;
  1132. begin
  1133. if FSystemModulesOnly then
  1134. begin
  1135. CurModule := LibModuleList;
  1136. while CurModule <> nil do
  1137. begin
  1138. CreateItemForAddress(Pointer(CurModule.Instance), True);
  1139. CurModule := CurModule.Next;
  1140. end;
  1141. end
  1142. else
  1143. begin
  1144. List := TStringList.Create;
  1145. try
  1146. LoadedModulesList(List, GetCurrentProcessId, True);
  1147. for I := 0 to List.Count - 1 do
  1148. CreateItemForAddress(List.Objects[I], False);
  1149. finally
  1150. List.Free;
  1151. end;
  1152. end;
  1153. //Sort(SortByStartAddress);
  1154. end;
  1155. function TJclModuleInfoList.CreateItemForAddress(Addr: Pointer; SystemModule: Boolean): TJclModuleInfo;
  1156. var
  1157. Module: HMODULE;
  1158. ModuleSize: DWORD;
  1159. begin
  1160. Result := nil;
  1161. Module := ModuleFromAddr(Addr);
  1162. if Module > 0 then
  1163. begin
  1164. ModuleSize := PeMapImgSize(Pointer(Module));
  1165. if ModuleSize <> 0 then
  1166. begin
  1167. Result := TJclModuleInfo.Create;
  1168. Result.FStartAddr := Pointer(Module);
  1169. Result.FSize := ModuleSize;
  1170. Result.FEndAddr := Pointer(Module + ModuleSize - 1);
  1171. if SystemModule then
  1172. Result.FSystemModule := True
  1173. else
  1174. Result.FSystemModule := IsSystemModule(Module);
  1175. end;
  1176. end;
  1177. if Result <> nil then
  1178. Add(Result);
  1179. end;
  1180. function TJclModuleInfoList.GetItems(Index: Integer): TJclModuleInfo;
  1181. begin
  1182. Result := TJclModuleInfo(Get(Index));
  1183. end;
  1184. function TJclModuleInfoList.GetModuleFromAddress(Addr: Pointer): TJclModuleInfo;
  1185. var
  1186. I: Integer;
  1187. Item: TJclModuleInfo;
  1188. begin
  1189. Result := nil;
  1190. for I := 0 to Count - 1 do
  1191. begin
  1192. Item := Items[I];
  1193. if (TJclAddr(Item.StartAddr) <= TJclAddr(Addr)) and (TJclAddr(Item.EndAddr) > TJclAddr(Addr)) then
  1194. begin
  1195. Result := Item;
  1196. Break;
  1197. end;
  1198. end;
  1199. if DynamicBuild and (Result = nil) then
  1200. Result := CreateItemForAddress(Addr, False);
  1201. end;
  1202. function TJclModuleInfoList.IsSystemModuleAddress(Addr: Pointer): Boolean;
  1203. var
  1204. Item: TJclModuleInfo;
  1205. begin
  1206. Item := ModuleFromAddress[Addr];
  1207. Result := (Item <> nil) and Item.SystemModule;
  1208. end;
  1209. function TJclModuleInfoList.IsValidModuleAddress(Addr: Pointer): Boolean;
  1210. begin
  1211. Result := ModuleFromAddress[Addr] <> nil;
  1212. end;
  1213. //=== { TJclAbstractMapParser } ==============================================
  1214. constructor TJclAbstractMapParser.Create(const MapFileName: TFileName; Module: HMODULE);
  1215. begin
  1216. inherited Create;
  1217. FModule := Module;
  1218. if FileExists(MapFileName) then
  1219. FStream := TJclFileMappingStream.Create(MapFileName, fmOpenRead or fmShareDenyWrite);
  1220. end;
  1221. constructor TJclAbstractMapParser.Create(const MapFileName: TFileName);
  1222. begin
  1223. Create(MapFileName, 0);
  1224. end;
  1225. destructor TJclAbstractMapParser.Destroy;
  1226. begin
  1227. FreeAndNil(FStream);
  1228. inherited Destroy;
  1229. end;
  1230. function TJclAbstractMapParser.GetLinkerBugUnitName: string;
  1231. begin
  1232. Result := MapStringToStr(FLinkerBugUnitName);
  1233. end;
  1234. class function TJclAbstractMapParser.MapStringToFileName(MapString: PJclMapString): string;
  1235. var
  1236. PEnd: PJclMapString;
  1237. begin
  1238. if MapString = nil then
  1239. begin
  1240. Result := '';
  1241. Exit;
  1242. end;
  1243. PEnd := MapString;
  1244. while (PEnd^ <> #0) and not (PEnd^ in ['=', #10, #13]) do
  1245. Inc(PEnd);
  1246. if (PEnd^ = '=') then
  1247. begin
  1248. while (PEnd >= MapString) and (PEnd^ <> ' ') do
  1249. Dec(PEnd);
  1250. while (PEnd >= MapString) and ((PEnd-1)^ = ' ') do
  1251. Dec(PEnd);
  1252. end;
  1253. SetString(Result, MapString, PEnd - MapString);
  1254. end;
  1255. class function TJclAbstractMapParser.MapStringToModuleName(MapString: PJclMapString): string;
  1256. var
  1257. PStart, PEnd, PExtension: PJclMapString;
  1258. begin
  1259. if MapString = nil then
  1260. begin
  1261. Result := '';
  1262. Exit;
  1263. end;
  1264. PEnd := MapString;
  1265. while (PEnd^ <> #0) and not (PEnd^ in ['=', #10, #13]) do
  1266. Inc(PEnd);
  1267. if (PEnd^ = '=') then
  1268. begin
  1269. while (PEnd >= MapString) and (PEnd^ <> ' ') do
  1270. Dec(PEnd);
  1271. while (PEnd >= MapString) and ((PEnd-1)^ = ' ') do
  1272. Dec(PEnd);
  1273. end;
  1274. PExtension := PEnd;
  1275. while (PExtension >= MapString) and (PExtension^ <> '.') and (PExtension^ <> '|') do
  1276. Dec(PExtension);
  1277. if (StrLICompA(PExtension, '.pas ', 5) = 0) or
  1278. (StrLICompA(PExtension, '.obj ', 5) = 0) then
  1279. PEnd := PExtension;
  1280. PExtension := PEnd;
  1281. while (PExtension >= MapString) and (PExtension^ <> '|') and (PExtension^ <> '\') do
  1282. Dec(PExtension);
  1283. if PExtension >= MapString then
  1284. PStart := PExtension + 1
  1285. else
  1286. PStart := MapString;
  1287. SetString(Result, PStart, PEnd - PStart);
  1288. end;
  1289. class function TJclAbstractMapParser.MapStringToStr(MapString: PJclMapString;
  1290. IgnoreSpaces: Boolean): string;
  1291. var
  1292. P: PJclMapString;
  1293. begin
  1294. if MapString = nil then
  1295. begin
  1296. Result := '';
  1297. Exit;
  1298. end;
  1299. if MapString^ = '(' then
  1300. begin
  1301. Inc(MapString);
  1302. P := MapString;
  1303. while (P^ <> #0) and not (P^ in [')', #10, #13]) do
  1304. Inc(P);
  1305. end
  1306. else
  1307. begin
  1308. P := MapString;
  1309. if IgnoreSpaces then
  1310. while (P^ <> #0) and not (P^ in ['(', #10, #13]) do
  1311. Inc(P)
  1312. else
  1313. while (P^ <> #0) and (P^ <> '(') and (P^ > ' ') do
  1314. Inc(P);
  1315. end;
  1316. SetString(Result, MapString, P - MapString);
  1317. end;
  1318. function IsDecDigit(P: PJclMapString): Boolean; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
  1319. begin
  1320. Result := False;
  1321. case P^ of
  1322. '0'..'9':
  1323. Result := True;
  1324. end;
  1325. end;
  1326. function SkipMapBlock(P, EndPos: PJclMapString): PJclMapString;
  1327. begin
  1328. Result := P;
  1329. while Result < EndPos do
  1330. begin
  1331. if not IsDecDigit(Result) then
  1332. Break;
  1333. Inc(Result);
  1334. // Skip to the end of the line
  1335. while Result < EndPos do
  1336. begin
  1337. case Result^ of
  1338. #10, #13:
  1339. Break;
  1340. end;
  1341. Inc(Result);
  1342. end;
  1343. // Skip WhiteSpaces
  1344. while (Result < EndPos) and (Result^ <= ' ') do
  1345. Inc(Result);
  1346. end;
  1347. end;
  1348. function AnsiStrPosIdxLen(const SubStr, S: PAnsiChar; Len: Integer): Integer;
  1349. var
  1350. I: Integer;
  1351. SubStrLen: Integer;
  1352. FirstCh: AnsiChar;
  1353. begin
  1354. Result := 0;
  1355. if Len = 0 then
  1356. Exit;
  1357. I := 0;
  1358. FirstCh := SubStr[0];
  1359. if FirstCh = #0 then
  1360. Exit;
  1361. SubStrLen := StrLenA(SubStr);
  1362. while I < Len do
  1363. begin
  1364. while (I < Len) and (S[I] <> FirstCh) do
  1365. Inc(I);
  1366. if I = Len then
  1367. Break;
  1368. if StrLCompA(SubStr, @S[I], SubStrLen) = 0 then
  1369. begin
  1370. Result := I + 1;
  1371. Exit;
  1372. end;
  1373. Inc(I);
  1374. end;
  1375. end;
  1376. procedure TJclAbstractMapParser.Parse;
  1377. const
  1378. TableHeader : array [0..3] of PJclMapString = ('Start', 'Length', 'Name', 'Class');
  1379. SegmentsHeader : array [0..3] of PJclMapString = ('Detailed', 'map', 'of', 'segments');
  1380. PublicsByNameHeader : array [0..3] of PJclMapString = ('Address', 'Publics', 'by', 'Name');
  1381. PublicsByValueHeader : array [0..3] of PJclMapString = ('Address', 'Publics', 'by', 'Value');
  1382. LineNumbersPrefix : PJclMapString = 'Line numbers for';
  1383. var
  1384. CurrPos, EndPos: PJclMapString;
  1385. {$IFNDEF COMPILER9_UP}
  1386. PreviousA,
  1387. {$ENDIF COMPILER9_UP}
  1388. A: TJclMapAddress;
  1389. L: Integer;
  1390. P1, P2: PJclMapString;
  1391. function Eof: Boolean;
  1392. begin
  1393. Result := CurrPos >= EndPos;
  1394. end;
  1395. function SkipWhiteSpace: PJclMapString;
  1396. var
  1397. LEndPos: PJclMapString;
  1398. begin
  1399. Result := CurrPos;
  1400. LEndPos := EndPos;
  1401. while (Result < LEndPos) and (Result^ <= ' ') do
  1402. Inc(Result);
  1403. CurrPos := Result;
  1404. end;
  1405. procedure SkipEndLine;
  1406. var
  1407. P, LEndPos: PJclMapString;
  1408. begin
  1409. P := CurrPos;
  1410. LEndPos := EndPos;
  1411. while P < LEndPos do
  1412. begin
  1413. case P^ of
  1414. #10, #13:
  1415. Break;
  1416. end;
  1417. Inc(P);
  1418. end;
  1419. // Skip WhiteSpaces
  1420. while (P < LEndPos) and (P^ <= ' ') do
  1421. Inc(P);
  1422. CurrPos := P;
  1423. end;
  1424. function ReadTrimmedTextLine(var Len: Integer): PJclMapString;
  1425. var
  1426. Start, P: PJclMapString;
  1427. begin
  1428. Start := CurrPos;
  1429. P := Start;
  1430. while (P^ <> #0) and not (P^ in [#10, #13]) do
  1431. Inc(P);
  1432. CurrPos := P;
  1433. // Trim
  1434. while (Start < P) and (Start^ <> #0) and (Start^ <= ' ') do
  1435. Inc(Start);
  1436. Dec(P);
  1437. while (P > Start) and (P^ <= ' ') do
  1438. Dec(P);
  1439. Inc(P);
  1440. Result := Start;
  1441. Len := P - Start;
  1442. if Len < 0 then
  1443. Len := 0;
  1444. end;
  1445. function ReadDecValue: Integer;
  1446. var
  1447. P: PJclMapString;
  1448. begin
  1449. P := CurrPos;
  1450. Result := 0;
  1451. while P^ in ['0'..'9'] do
  1452. begin
  1453. Result := Result * 10 + (Ord(P^) - Ord('0'));
  1454. Inc(P);
  1455. end;
  1456. CurrPos := P;
  1457. end;
  1458. function ReadHexValue: DWORD;
  1459. var
  1460. C: AnsiChar;
  1461. V: Byte;
  1462. P: PJclMapString;
  1463. begin
  1464. P := CurrPos;
  1465. Result := 0;
  1466. repeat
  1467. C := P^;
  1468. V := HexMap[C];
  1469. if V and $80 <> 0 then
  1470. Break;
  1471. Result := (Result shl 4) or V;
  1472. Inc(P);
  1473. until False;
  1474. if (C = 'H') or (C = 'h') then
  1475. Inc(P);
  1476. CurrPos := P;
  1477. end;
  1478. procedure ReadAddress(var Result: TJclMapAddress);
  1479. begin
  1480. Result.Segment := ReadHexValue;
  1481. if CurrPos^ = ':' then
  1482. begin
  1483. Inc(CurrPos);
  1484. Result.Offset := ReadHexValue;
  1485. end
  1486. else
  1487. Result.Offset := 0;
  1488. end;
  1489. function ReadString: PJclMapString;
  1490. var
  1491. P, LEndPos: PJclMapString;
  1492. begin
  1493. // Skip WhiteSpaces
  1494. LEndPos := EndPos;
  1495. P := CurrPos;
  1496. while (P < LEndPos) and (P^ <= ' ') do
  1497. Inc(P);
  1498. Result := P;
  1499. while {(P^ <> #0) and} (P^ > ' ') do
  1500. Inc(P);
  1501. CurrPos := P;
  1502. end;
  1503. procedure FindParam(Param: AnsiChar);
  1504. var
  1505. P: PJclMapString;
  1506. begin
  1507. P := CurrPos;
  1508. while not ((P^ = Param) and (P[1] = '=')) do
  1509. Inc(P);
  1510. CurrPos := P + 2;
  1511. end;
  1512. function SyncToHeader(const Header: array of PJclMapString): Boolean;
  1513. var
  1514. S: PJclMapString;
  1515. SLen: Integer;
  1516. TokenIndex, OldPosition, CurrentPosition: Integer;
  1517. begin
  1518. Result := False;
  1519. while not Eof do
  1520. begin
  1521. S := ReadTrimmedTextLine(SLen);
  1522. if SLen > 0 then
  1523. begin
  1524. TokenIndex := Low(Header);
  1525. CurrentPosition := 0;
  1526. OldPosition := 0;
  1527. while (TokenIndex <= High(Header)) do
  1528. begin
  1529. CurrentPosition := AnsiStrPosIdxLen(Header[TokenIndex], S, SLen);
  1530. if (CurrentPosition <= OldPosition) then
  1531. begin
  1532. CurrentPosition := 0;
  1533. Break;
  1534. end;
  1535. OldPosition := CurrentPosition;
  1536. Inc(TokenIndex);
  1537. end;
  1538. Result := CurrentPosition <> 0;
  1539. if Result then
  1540. Break;
  1541. end;
  1542. SkipEndLine;
  1543. end;
  1544. if not Eof then
  1545. SkipWhiteSpace;
  1546. end;
  1547. function SyncToPrefix(const Prefix: PJclMapString): Boolean;
  1548. var
  1549. P: PJclMapString;
  1550. PrefixLen: Integer;
  1551. begin
  1552. if Eof then
  1553. begin
  1554. Result := False;
  1555. Exit;
  1556. end;
  1557. SkipWhiteSpace;
  1558. P := CurrPos;
  1559. PrefixLen := StrLenA(Prefix);
  1560. Result := StrLCompA(Prefix, P, PrefixLen) = 0;
  1561. if Result then
  1562. CurrPos := P + PrefixLen;
  1563. SkipWhiteSpace;
  1564. end;
  1565. begin
  1566. if FStream <> nil then
  1567. begin
  1568. FLinkerBug := False;
  1569. {$IFNDEF COMPILER9_UP}
  1570. PreviousA.Segment := 0;
  1571. PreviousA.Offset := 0;
  1572. {$ENDIF COMPILER9_UP}
  1573. CurrPos := FStream.Memory;
  1574. EndPos := CurrPos + FStream.Size;
  1575. if SyncToHeader(TableHeader) then
  1576. while IsDecDigit(CurrPos) do
  1577. begin
  1578. ReadAddress(A);
  1579. SkipWhiteSpace;
  1580. L := ReadHexValue;
  1581. P1 := ReadString;
  1582. P2 := ReadString;
  1583. SkipEndLine;
  1584. ClassTableItem(A, L, P1, P2);
  1585. end;
  1586. if SyncToHeader(SegmentsHeader) then
  1587. while IsDecDigit(CurrPos) do
  1588. begin
  1589. ReadAddress(A);
  1590. SkipWhiteSpace;
  1591. L := ReadHexValue;
  1592. FindParam('C');
  1593. P1 := ReadString;
  1594. FindParam('M');
  1595. P2 := ReadString;
  1596. SkipEndLine;
  1597. SegmentItem(A, L, P1, P2);
  1598. end;
  1599. if SyncToHeader(PublicsByNameHeader) then
  1600. begin
  1601. if not CanHandlePublicsByName then
  1602. CurrPos := SkipMapBlock(CurrPos, EndPos)
  1603. else
  1604. begin
  1605. while IsDecDigit(CurrPos) do
  1606. begin
  1607. ReadAddress(A);
  1608. P1 := ReadString;
  1609. SkipEndLine; // compatibility with C++Builder MAP files
  1610. PublicsByNameItem(A, P1);
  1611. end;
  1612. end;
  1613. end;
  1614. if SyncToHeader(PublicsByValueHeader) then
  1615. if not CanHandlePublicsByValue then
  1616. CurrPos := SkipMapBlock(CurrPos, EndPos)
  1617. else
  1618. begin
  1619. while not Eof and IsDecDigit(CurrPos) do
  1620. begin
  1621. ReadAddress(A);
  1622. P1 := ReadString;
  1623. SkipEndLine; // compatibility with C++Builder MAP files
  1624. PublicsByValueItem(A, P1);
  1625. end;
  1626. end;
  1627. while SyncToPrefix(LineNumbersPrefix) do
  1628. begin
  1629. FLastUnitName := CurrPos;
  1630. FLastUnitFileName := CurrPos;
  1631. while FLastUnitFileName^ <> '(' do
  1632. Inc(FLastUnitFileName);
  1633. SkipEndLine;
  1634. LineNumberUnitItem(FLastUnitName, FLastUnitFileName);
  1635. repeat
  1636. SkipWhiteSpace;
  1637. L := ReadDecValue;
  1638. SkipWhiteSpace;
  1639. ReadAddress(A);
  1640. SkipWhiteSpace;
  1641. LineNumbersItem(L, A);
  1642. {$IFNDEF COMPILER9_UP}
  1643. if not FLinkerBug and (A.Offset < PreviousA.Offset) then
  1644. begin
  1645. FLinkerBugUnitName := FLastUnitName;
  1646. FLinkerBug := True;
  1647. end;
  1648. PreviousA := A;
  1649. {$ENDIF COMPILER9_UP}
  1650. until not IsDecDigit(CurrPos);
  1651. end;
  1652. end;
  1653. end;
  1654. //=== { TJclMapParser } ======================================================
  1655. procedure TJclMapParser.ClassTableItem(const Address: TJclMapAddress;
  1656. Len: Integer; SectionName, GroupName: PJclMapString);
  1657. begin
  1658. if Assigned(FOnClassTable) then
  1659. FOnClassTable(Self, Address, Len, MapStringToStr(SectionName), MapStringToStr(GroupName));
  1660. end;
  1661. procedure TJclMapParser.LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress);
  1662. begin
  1663. if Assigned(FOnLineNumbers) then
  1664. FOnLineNumbers(Self, LineNumber, Address);
  1665. end;
  1666. procedure TJclMapParser.LineNumberUnitItem(UnitName, UnitFileName: PJclMapString);
  1667. begin
  1668. if Assigned(FOnLineNumberUnit) then
  1669. FOnLineNumberUnit(Self, MapStringToStr(UnitName), MapStringToStr(UnitFileName));
  1670. end;
  1671. function TJclMapParser.CanHandlePublicsByName: Boolean;
  1672. begin
  1673. Result := Assigned(FOnPublicsByName);
  1674. end;
  1675. function TJclMapParser.CanHandlePublicsByValue: Boolean;
  1676. begin
  1677. Result := Assigned(FOnPublicsByValue);
  1678. end;
  1679. procedure TJclMapParser.PublicsByNameItem(const Address: TJclMapAddress;
  1680. Name: PJclMapString);
  1681. begin
  1682. if Assigned(FOnPublicsByName) then
  1683. // MAP files generated by C++Builder have spaces in their identifier names
  1684. FOnPublicsByName(Self, Address, MapStringToStr(Name, True));
  1685. end;
  1686. procedure TJclMapParser.PublicsByValueItem(const Address: TJclMapAddress;
  1687. Name: PJclMapString);
  1688. begin
  1689. if Assigned(FOnPublicsByValue) then
  1690. // MAP files generated by C++Builder have spaces in their identifier names
  1691. FOnPublicsByValue(Self, Address, MapStringToStr(Name, True));
  1692. end;
  1693. procedure TJclMapParser.SegmentItem(const Address: TJclMapAddress;
  1694. Len: Integer; GroupName, UnitName: PJclMapString);
  1695. begin
  1696. if Assigned(FOnSegmentItem) then
  1697. FOnSegmentItem(Self, Address, Len, MapStringToStr(GroupName), MapStringToModuleName(UnitName));
  1698. end;
  1699. //=== { TJclMapScanner } =====================================================
  1700. constructor TJclMapScanner.Create(const MapFileName: TFileName; Module: HMODULE);
  1701. begin
  1702. inherited Create(MapFileName, Module);
  1703. Scan;
  1704. end;
  1705. function TJclMapScanner.MAPAddrToVA(const Addr: DWORD): DWORD;
  1706. begin
  1707. // MAP file format was changed in Delphi 2005
  1708. // before Delphi 2005: segments started at offset 0
  1709. // only one segment of code
  1710. // after Delphi 2005: segments started at code base address (module base address + $10000)
  1711. // 2 segments of code
  1712. if (Length(FSegmentClasses) > 0) and (FSegmentClasses[0].Start > 0) and (Addr >= FSegmentClasses[0].Start) then
  1713. // Delphi 2005 and later
  1714. // The first segment should be code starting at module base address + $10000
  1715. Result := Addr - FSegmentClasses[0].Start
  1716. else
  1717. // before Delphi 2005
  1718. Result := Addr;
  1719. end;
  1720. class function TJclMapScanner.MapStringCacheToFileName(
  1721. var MapString: TJclMapStringCache): string;
  1722. begin
  1723. Result := MapString.CachedValue;
  1724. if Result = '' then
  1725. begin
  1726. Result := MapStringToFileName(MapString.RawValue);
  1727. MapString.CachedValue := Result;
  1728. end;
  1729. end;
  1730. class function TJclMapScanner.MapStringCacheToModuleName(
  1731. var MapString: TJclMapStringCache): string;
  1732. begin
  1733. Result := MapString.CachedValue;
  1734. if Result = '' then
  1735. begin
  1736. Result := MapStringToModuleName(MapString.RawValue);
  1737. MapString.CachedValue := Result;
  1738. end;
  1739. end;
  1740. class function TJclMapScanner.MapStringCacheToStr(var MapString: TJclMapStringCache;
  1741. IgnoreSpaces: Boolean): string;
  1742. begin
  1743. Result := MapString.CachedValue;
  1744. if Result = '' then
  1745. begin
  1746. Result := MapStringToStr(MapString.RawValue, IgnoreSpaces);
  1747. MapString.CachedValue := Result;
  1748. end;
  1749. end;
  1750. procedure TJclMapScanner.ClassTableItem(const Address: TJclMapAddress; Len: Integer;
  1751. SectionName, GroupName: PJclMapString);
  1752. var
  1753. C: Integer;
  1754. SectionHeader: PImageSectionHeader;
  1755. begin
  1756. C := Length(FSegmentClasses);
  1757. SetLength(FSegmentClasses, C + 1);
  1758. FSegmentClasses[C].Segment := Address.Segment;
  1759. FSegmentClasses[C].Start := Address.Offset;
  1760. FSegmentClasses[C].Addr := Address.Offset; // will be fixed below while considering module mapped address
  1761. // test GroupName because SectionName = '.tls' in Delphi and '_tls' in BCB
  1762. if StrLICompA(GroupName, 'TLS', 3) = 0 then
  1763. begin
  1764. FSegmentClasses[C].VA := FSegmentClasses[C].Start;
  1765. FSegmentClasses[C].GroupName.TLS := True;
  1766. end
  1767. else
  1768. begin
  1769. FSegmentClasses[C].VA := MAPAddrToVA(FSegmentClasses[C].Start);
  1770. FSegmentClasses[C].GroupName.TLS := False;
  1771. end;
  1772. FSegmentClasses[C].Len := Len;
  1773. FSegmentClasses[C].SectionName.RawValue := SectionName;
  1774. FSegmentClasses[C].GroupName.RawValue := GroupName;
  1775. if FModule <> 0 then
  1776. begin
  1777. { Fix the section addresses }
  1778. SectionHeader := PeMapImgFindSectionFromModule(Pointer(FModule), MapStringToStr(SectionName));
  1779. if SectionHeader = nil then
  1780. { before Delphi 2005 the class names where used for the section names }
  1781. SectionHeader := PeMapImgFindSectionFromModule(Pointer(FModule), MapStringToStr(GroupName));
  1782. if SectionHeader <> nil then
  1783. begin
  1784. FSegmentClasses[C].Addr := TJclAddr(FModule) + SectionHeader.VirtualAddress;
  1785. FSegmentClasses[C].VA := SectionHeader.VirtualAddress;
  1786. end;
  1787. end;
  1788. end;
  1789. function TJclMapScanner.LineNumberFromAddr(Addr: DWORD): Integer;
  1790. var
  1791. Dummy: Integer;
  1792. begin
  1793. Result := LineNumberFromAddr(Addr, Dummy);
  1794. end;
  1795. function Search_MapLineNumber(Item1, Item2: Pointer): Integer;
  1796. begin
  1797. Result := Integer(PJclMapLineNumber(Item1)^.VA) - PInteger(Item2)^;
  1798. end;
  1799. function TJclMapScanner.LineNumberFromAddr(Addr: DWORD; out Offset: Integer): Integer;
  1800. var
  1801. I: Integer;
  1802. ModuleStartAddr: DWORD;
  1803. begin
  1804. ModuleStartAddr := ModuleStartFromAddr(Addr);
  1805. Result := 0;
  1806. Offset := 0;
  1807. I := SearchDynArray(FLineNumbers, SizeOf(FLineNumbers[0]), Search_MapLineNumber, @Addr, True);
  1808. if (I <> -1) and (FLineNumbers[I].VA >= ModuleStartAddr) then
  1809. begin
  1810. Result := FLineNumbers[I].LineNumber;
  1811. Offset := Addr - FLineNumbers[I].VA;
  1812. end;
  1813. end;
  1814. procedure TJclMapScanner.LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress);
  1815. var
  1816. SegIndex, C: Integer;
  1817. VA: DWORD;
  1818. Added: Boolean;
  1819. begin
  1820. Added := False;
  1821. for SegIndex := Low(FSegmentClasses) to High(FSegmentClasses) do
  1822. if (FSegmentClasses[SegIndex].Segment = Address.Segment)
  1823. and (DWORD(Address.Offset) < FSegmentClasses[SegIndex].Len) then
  1824. begin
  1825. if FSegmentClasses[SegIndex].GroupName.TLS then
  1826. Va := Address.Offset
  1827. else
  1828. VA := MAPAddrToVA(Address.Offset + FSegmentClasses[SegIndex].Start);
  1829. { Starting with Delphi 2005, "empty" units are listes with the last line and
  1830. the VA 0001:00000000. When we would accept 0 VAs here, System.pas functions
  1831. could be mapped to other units and line numbers. Discaring such items should
  1832. have no impact on the correct information, because there can't be a function
  1833. that starts at VA 0. }
  1834. if VA = 0 then
  1835. Continue;
  1836. if FLineNumbersCnt = Length(FLineNumbers) then
  1837. begin
  1838. if FLineNumbersCnt < 512 then
  1839. SetLength(FLineNumbers, FLineNumbersCnt + 512)
  1840. else
  1841. SetLength(FLineNumbers, FLineNumbersCnt * 2);
  1842. end;
  1843. FLineNumbers[FLineNumbersCnt].Segment := FSegmentClasses[SegIndex].Segment;
  1844. FLineNumbers[FLineNumbersCnt].VA := VA;
  1845. FLineNumbers[FLineNumbersCnt].LineNumber := LineNumber;
  1846. FLineNumbers[FLineNumbersCnt].UnitName := FCurrentUnitName;
  1847. Inc(FLineNumbersCnt);
  1848. Added := True;
  1849. if FNewUnitFileName <> nil then
  1850. begin
  1851. C := Length(FSourceNames);
  1852. SetLength(FSourceNames, C + 1);
  1853. FSourceNames[C].Segment := FSegmentClasses[SegIndex].Segment;
  1854. FSourceNames[C].VA := VA;
  1855. FSourceNames[C].ProcName.RawValue := FNewUnitFileName;
  1856. FNewUnitFileName := nil;
  1857. end;
  1858. Break;
  1859. end;
  1860. if not Added then
  1861. Inc(FLineNumberErrors);
  1862. end;
  1863. procedure TJclMapScanner.LineNumberUnitItem(UnitName, UnitFileName: PJclMapString);
  1864. begin
  1865. FNewUnitFileName := UnitFileName;
  1866. FCurrentUnitName := UnitName;
  1867. end;
  1868. function TJclMapScanner.GetLineNumberByIndex(Index: Integer): TJCLMapLineNumber;
  1869. begin
  1870. Result := FLineNumbers[Index];
  1871. end;
  1872. function TJclMapScanner.IndexOfSegment(Addr: DWORD): Integer;
  1873. var
  1874. L, R: Integer;
  1875. S: PJclMapSegment;
  1876. begin
  1877. R := Length(FSegments) - 1;
  1878. Result := FLastAccessedSegementIndex;
  1879. if Result <= R then
  1880. begin
  1881. S := @FSegments[Result];
  1882. if (S.StartVA <= Addr) and (Addr < S.EndVA) then
  1883. Exit;
  1884. end;
  1885. // binary search
  1886. L := 0;
  1887. while L <= R do
  1888. begin
  1889. Result := L + (R - L) div 2;
  1890. S := @FSegments[Result];
  1891. if Addr >= S.EndVA then
  1892. L := Result + 1
  1893. else
  1894. begin
  1895. R := Result - 1;
  1896. if (S.StartVA <= Addr) and (Addr < S.EndVA) then
  1897. begin
  1898. FLastAccessedSegementIndex := Result;
  1899. Exit;
  1900. end;
  1901. end;
  1902. end;
  1903. Result := -1;
  1904. end;
  1905. function TJclMapScanner.ModuleNameFromAddr(Addr: DWORD): string;
  1906. var
  1907. I: Integer;
  1908. begin
  1909. I := IndexOfSegment(Addr);
  1910. if I <> -1 then
  1911. Result := MapStringCacheToModuleName(FSegments[I].UnitName)
  1912. else
  1913. Result := '';
  1914. end;
  1915. function TJclMapScanner.ModuleStartFromAddr(Addr: DWORD): DWORD;
  1916. var
  1917. I: Integer;
  1918. begin
  1919. I := IndexOfSegment(Addr);
  1920. Result := DWORD(-1);
  1921. if I <> -1 then
  1922. Result := FSegments[I].StartVA;
  1923. end;
  1924. function TJclMapScanner.ProcNameFromAddr(Addr: DWORD): string;
  1925. var
  1926. Dummy: Integer;
  1927. begin
  1928. Result := ProcNameFromAddr(Addr, Dummy);
  1929. end;
  1930. function Search_MapProcName(Item1, Item2: Pointer): Integer;
  1931. begin
  1932. Result := Integer(PJclMapProcName(Item1)^.VA) - PInteger(Item2)^;
  1933. end;
  1934. function TJclMapScanner.ProcNameFromAddr(Addr: DWORD; out Offset: Integer): string;
  1935. var
  1936. I: Integer;
  1937. ModuleStartAddr: DWORD;
  1938. begin
  1939. ModuleStartAddr := ModuleStartFromAddr(Addr);
  1940. Result := '';
  1941. Offset := 0;
  1942. I := SearchDynArray(FProcNames, SizeOf(FProcNames[0]), Search_MapProcName, @Addr, True);
  1943. if (I <> -1) and (FProcNames[I].VA >= ModuleStartAddr) then
  1944. begin
  1945. Result := MapStringCacheToStr(FProcNames[I].ProcName, True);
  1946. Offset := Addr - FProcNames[I].VA;
  1947. end;
  1948. end;
  1949. function TJclMapScanner.CanHandlePublicsByName: Boolean;
  1950. begin
  1951. Result := False;
  1952. end;
  1953. function TJclMapScanner.CanHandlePublicsByValue: Boolean;
  1954. begin
  1955. Result := True;
  1956. end;
  1957. procedure TJclMapScanner.PublicsByNameItem(const Address: TJclMapAddress; Name: PJclMapString);
  1958. begin
  1959. end;
  1960. procedure TJclMapScanner.PublicsByValueItem(const Address: TJclMapAddress; Name: PJclMapString);
  1961. var
  1962. SegIndex: Integer;
  1963. begin
  1964. for SegIndex := Low(FSegmentClasses) to High(FSegmentClasses) do
  1965. if (FSegmentClasses[SegIndex].Segment = Address.Segment)
  1966. and (DWORD(Address.Offset) < FSegmentClasses[SegIndex].Len) then
  1967. begin
  1968. if FProcNamesCnt = Length(FProcNames) then
  1969. begin
  1970. if FProcNamesCnt < 512 then
  1971. SetLength(FProcNames, FProcNamesCnt + 512)
  1972. else
  1973. SetLength(FProcNames, FProcNamesCnt * 2);
  1974. end;
  1975. FProcNames[FProcNamesCnt].Segment := FSegmentClasses[SegIndex].Segment;
  1976. if FSegmentClasses[SegIndex].GroupName.TLS then
  1977. FProcNames[FProcNamesCnt].VA := Address.Offset
  1978. else
  1979. FProcNames[FProcNamesCnt].VA := MAPAddrToVA(Address.Offset + FSegmentClasses[SegIndex].Start);
  1980. FProcNames[FProcNamesCnt].ProcName.RawValue := Name;
  1981. Inc(FProcNamesCnt);
  1982. Break;
  1983. end;
  1984. end;
  1985. {function Sort_MapLineNumber(Item1, Item2: Pointer): Integer;
  1986. begin
  1987. Result := Integer(PJclMapLineNumber(Item1)^.VA) - Integer(PJclMapLineNumber(Item2)^.VA);
  1988. end;}
  1989. function Sort_MapProcName(Item1, Item2: Pointer): Integer;
  1990. begin
  1991. Result := Integer(PJclMapProcName(Item1)^.VA) - Integer(PJclMapProcName(Item2)^.VA);
  1992. end;
  1993. function Sort_MapSegment(Item1, Item2: Pointer): Integer;
  1994. begin
  1995. Result := Integer(PJclMapSegment(Item1)^.EndVA) - Integer(PJclMapSegment(Item2)^.EndVA);
  1996. if Result = 0 then
  1997. Result := Integer(PJclMapSegment(Item1)^.StartVA) - Integer(PJclMapSegment(Item2)^.StartVA);
  1998. end;
  1999. type
  2000. PJclMapLineNumberArray = ^TJclMapLineNumberArray;
  2001. TJclMapLineNumberArray = array[0..MaxInt div SizeOf(TJclMapLineNumber) - 1] of TJclMapLineNumber;
  2002. PJclMapProcNameArray = ^TJclMapProcNameArray;
  2003. TJclMapProcNameArray = array[0..MaxInt div SizeOf(TJclMapProcName) - 1] of TJclMapProcName;
  2004. // specialized quicksort functions
  2005. procedure SortLineNumbers(ArrayVar: PJclMapLineNumberArray; L, R: Integer);
  2006. var
  2007. I, J, P: Integer;
  2008. Temp: TJclMapLineNumber;
  2009. AV: PJclMapLineNumber;
  2010. V: Integer;
  2011. begin
  2012. repeat
  2013. I := L;
  2014. J := R;
  2015. P := (L + R) shr 1;
  2016. repeat
  2017. V := Integer(ArrayVar[P].VA);
  2018. AV := @ArrayVar[I];
  2019. while Integer(AV.VA) - V < 0 do begin Inc(I); Inc(AV); end;
  2020. AV := @ArrayVar[J];
  2021. while Integer(AV.VA) - V > 0 do begin Dec(J); Dec(AV); end;
  2022. if I <= J then
  2023. begin
  2024. if I <> J then
  2025. begin
  2026. Temp := ArrayVar[I];
  2027. ArrayVar[I] := ArrayVar[J];
  2028. ArrayVar[J] := Temp;
  2029. end;
  2030. if P = I then
  2031. P := J
  2032. else if P = J then
  2033. P := I;
  2034. Inc(I);
  2035. Dec(J);
  2036. end;
  2037. until I > J;
  2038. if L < J then
  2039. SortLineNumbers(ArrayVar, L, J);
  2040. L := I;
  2041. until I >= R;
  2042. end;
  2043. procedure SortProcNames(ArrayVar: PJclMapProcNameArray; L, R: Integer);
  2044. var
  2045. I, J, P: Integer;
  2046. Temp: TJclMapProcName;
  2047. V: Integer;
  2048. AV: PJclMapProcName;
  2049. begin
  2050. repeat
  2051. I := L;
  2052. J := R;
  2053. P := (L + R) shr 1;
  2054. repeat
  2055. V := Integer(ArrayVar[P].VA);
  2056. AV := @ArrayVar[I];
  2057. while Integer(AV.VA) - V < 0 do begin Inc(I); Inc(AV); end;
  2058. AV := @ArrayVar[J];
  2059. while Integer(AV.VA) - V > 0 do begin Dec(J); Dec(AV); end;
  2060. if I <= J then
  2061. begin
  2062. if I <> J then
  2063. begin
  2064. Temp := ArrayVar[I];
  2065. ArrayVar[I] := ArrayVar[J];
  2066. ArrayVar[J] := Temp;
  2067. end;
  2068. if P = I then
  2069. P := J
  2070. else if P = J then
  2071. P := I;
  2072. Inc(I);
  2073. Dec(J);
  2074. end;
  2075. until I > J;
  2076. if L < J then
  2077. SortProcNames(ArrayVar, L, J);
  2078. L := I;
  2079. until I >= R;
  2080. end;
  2081. procedure TJclMapScanner.Scan;
  2082. begin
  2083. FLineNumberErrors := 0;
  2084. FSegmentCnt := 0;
  2085. FProcNamesCnt := 0;
  2086. FLastAccessedSegementIndex := 0;
  2087. Parse;
  2088. SetLength(FLineNumbers, FLineNumbersCnt);
  2089. SetLength(FProcNames, FProcNamesCnt);
  2090. SetLength(FSegments, FSegmentCnt);
  2091. //SortDynArray(FLineNumbers, SizeOf(FLineNumbers[0]), Sort_MapLineNumber);
  2092. if FLineNumbers <> nil then
  2093. SortLineNumbers(PJclMapLineNumberArray(FLineNumbers), 0, Length(FLineNumbers) - 1);
  2094. //SortDynArray(FProcNames, SizeOf(FProcNames[0]), Sort_MapProcName);
  2095. if FProcNames <> nil then
  2096. SortProcNames(PJclMapProcNameArray(FProcNames), 0, Length(FProcNames) - 1);
  2097. SortDynArray(FSegments, SizeOf(FSegments[0]), Sort_MapSegment);
  2098. SortDynArray(FSourceNames, SizeOf(FSourceNames[0]), Sort_MapProcName);
  2099. end;
  2100. procedure TJclMapScanner.SegmentItem(const Address: TJclMapAddress; Len: Integer;
  2101. GroupName, UnitName: PJclMapString);
  2102. var
  2103. SegIndex: Integer;
  2104. VA: DWORD;
  2105. begin
  2106. for SegIndex := Low(FSegmentClasses) to High(FSegmentClasses) do
  2107. if (FSegmentClasses[SegIndex].Segment = Address.Segment)
  2108. and (DWORD(Address.Offset) < FSegmentClasses[SegIndex].Len) then
  2109. begin
  2110. if FSegmentClasses[SegIndex].GroupName.TLS then
  2111. VA := Address.Offset
  2112. else
  2113. VA := MAPAddrToVA(Address.Offset + FSegmentClasses[SegIndex].Start);
  2114. if FSegmentCnt mod 16 = 0 then
  2115. SetLength(FSegments, FSegmentCnt + 16);
  2116. FSegments[FSegmentCnt].Segment := FSegmentClasses[SegIndex].Segment;
  2117. FSegments[FSegmentCnt].StartVA := VA;
  2118. FSegments[FSegmentCnt].EndVA := VA + DWORD(Len);
  2119. FSegments[FSegmentCnt].UnitName.RawValue := UnitName;
  2120. Inc(FSegmentCnt);
  2121. Break;
  2122. end;
  2123. end;
  2124. function TJclMapScanner.SourceNameFromAddr(Addr: DWORD): string;
  2125. var
  2126. I: Integer;
  2127. ModuleStartVA: DWORD;
  2128. begin
  2129. // try with line numbers first (Delphi compliance)
  2130. ModuleStartVA := ModuleStartFromAddr(Addr);
  2131. Result := '';
  2132. I := SearchDynArray(FSourceNames, SizeOf(FSourceNames[0]), Search_MapProcName, @Addr, True);
  2133. if (I <> -1) and (FSourceNames[I].VA >= ModuleStartVA) then
  2134. Result := MapStringCacheToStr(FSourceNames[I].ProcName);
  2135. if Result = '' then
  2136. begin
  2137. // try with module names (C++Builder compliance)
  2138. I := IndexOfSegment(Addr);
  2139. if I <> -1 then
  2140. Result := MapStringCacheToFileName(FSegments[I].UnitName);
  2141. end;
  2142. end;
  2143. function TJclMapScanner.VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;
  2144. var
  2145. I: Integer;
  2146. QualifiedName: string;
  2147. begin
  2148. Result := 0;
  2149. if (UnitName = '') or (ProcName = '') then
  2150. Exit;
  2151. QualifiedName := UnitName + '.' + ProcName;
  2152. for I := Low(FProcNames) to High(FProcNames) do
  2153. begin
  2154. if CompareText(MapStringCacheToStr(FProcNames[I].ProcName, True), QualifiedName) = 0 then
  2155. begin
  2156. Result := FProcNames[i].VA;
  2157. Break;
  2158. end;
  2159. end;
  2160. end;
  2161. // JCL binary debug format string encoding/decoding routines
  2162. { Strings are compressed to following 6bit format (A..D represents characters) and terminated with }
  2163. { 6bit #0 char. First char = #1 indicates non compressed text, #2 indicates compressed text with }
  2164. { leading '@' character }
  2165. { }
  2166. { 7 6 5 4 3 2 1 0 | }
  2167. {--------------------------------- }
  2168. { B1 B0 A5 A4 A3 A2 A1 A0 | Data byte 0 }
  2169. {--------------------------------- }
  2170. { C3 C2 C1 C0 B5 B4 B3 B2 | Data byte 1 }
  2171. {--------------------------------- }
  2172. { D5 D4 D3 D2 D1 D0 C5 C4 | Data byte 2 }
  2173. {--------------------------------- }
  2174. function SimpleCryptString(const S: TUTF8String): TUTF8String;
  2175. var
  2176. I: Integer;
  2177. C: Byte;
  2178. P: PByte;
  2179. begin
  2180. SetLength(Result, Length(S));
  2181. P := PByte(Result);
  2182. for I := 1 to Length(S) do
  2183. begin
  2184. C := Ord(S[I]);
  2185. if C <> $AA then
  2186. C := C xor $AA;
  2187. P^ := C;
  2188. Inc(P);
  2189. end;
  2190. end;
  2191. function DecodeNameString(const S: PAnsiChar): string;
  2192. var
  2193. I, B: Integer;
  2194. C: Byte;
  2195. P: PByte;
  2196. Buffer: array [0..255] of AnsiChar;
  2197. begin
  2198. Result := '';
  2199. B := 0;
  2200. P := PByte(S);
  2201. case P^ of
  2202. 1:
  2203. begin
  2204. Inc(P);
  2205. Result := UTF8ToString(SimpleCryptString(PAnsiChar(P)));
  2206. Exit;
  2207. end;
  2208. 2:
  2209. begin
  2210. Inc(P);
  2211. Buffer[B] := '@';
  2212. Inc(B);
  2213. end;
  2214. end;
  2215. I := 0;
  2216. C := 0;
  2217. repeat
  2218. case I and $03 of
  2219. 0:
  2220. C := P^ and $3F;
  2221. 1:
  2222. begin
  2223. C := (P^ shr 6) and $03;
  2224. Inc(P);
  2225. Inc(C, (P^ and $0F) shl 2);
  2226. end;
  2227. 2:
  2228. begin
  2229. C := (P^ shr 4) and $0F;
  2230. Inc(P);
  2231. Inc(C, (P^ and $03) shl 4);
  2232. end;
  2233. 3:
  2234. begin
  2235. C := (P^ shr 2) and $3F;
  2236. Inc(P);
  2237. end;
  2238. end;
  2239. case C of
  2240. $00:
  2241. Break;
  2242. $01..$0A:
  2243. Inc(C, Ord('0') - $01);
  2244. $0B..$24:
  2245. Inc(C, Ord('A') - $0B);
  2246. $25..$3E:
  2247. Inc(C, Ord('a') - $25);
  2248. $3F:
  2249. C := Ord('_');
  2250. end;
  2251. Buffer[B] := AnsiChar(C);
  2252. Inc(B);
  2253. Inc(I);
  2254. until B >= SizeOf(Buffer) - 1;
  2255. Buffer[B] := #0;
  2256. Result := UTF8ToString(Buffer);
  2257. end;
  2258. function EncodeNameString(const S: string): AnsiString;
  2259. var
  2260. I, StartIndex, EndIndex: Integer;
  2261. C: Byte;
  2262. P: PByte;
  2263. begin
  2264. if (Length(S) > 1) and (S[1] = '@') then
  2265. StartIndex := 1
  2266. else
  2267. StartIndex := 0;
  2268. for I := StartIndex + 1 to Length(S) do
  2269. if not CharIsValidIdentifierLetter(Char(S[I])) then
  2270. begin
  2271. {$IFDEF SUPPORTS_UNICODE}
  2272. Result := #1 + SimpleCryptString(UTF8Encode(S)) + #0; // UTF8Encode is much faster than StringToUTF8
  2273. {$ELSE}
  2274. Result := #1 + SimpleCryptString(StringToUTF8(S)) + #0;
  2275. {$ENDIF SUPPORTS_UNICODE}
  2276. Exit;
  2277. end;
  2278. SetLength(Result, Length(S) + StartIndex);
  2279. P := Pointer(Result);
  2280. if StartIndex = 1 then
  2281. P^ := 2 // store '@' leading char information
  2282. else
  2283. Dec(P);
  2284. EndIndex := Length(S) - StartIndex;
  2285. for I := 0 to EndIndex do // including null char
  2286. begin
  2287. if I = EndIndex then
  2288. C := 0
  2289. else
  2290. C := Byte(S[I + 1 + StartIndex]);
  2291. case AnsiChar(C) of
  2292. #0:
  2293. C := 0;
  2294. '0'..'9':
  2295. Dec(C, Ord('0') - $01);
  2296. 'A'..'Z':
  2297. Dec(C, Ord('A') - $0B);
  2298. 'a'..'z':
  2299. Dec(C, Ord('a') - $25);
  2300. '_':
  2301. C := $3F;
  2302. else
  2303. C := $3F;
  2304. end;
  2305. case I and $03 of
  2306. 0:
  2307. begin
  2308. Inc(P);
  2309. P^ := C;
  2310. end;
  2311. 1:
  2312. begin
  2313. P^ := P^ or (C and $03) shl 6;
  2314. Inc(P);
  2315. P^ := (C shr 2) and $0F;
  2316. end;
  2317. 2:
  2318. begin
  2319. P^ := P^ or Byte(C shl 4);
  2320. Inc(P);
  2321. P^ := (C shr 4) and $03;
  2322. end;
  2323. 3:
  2324. P^ := P^ or (C shl 2);
  2325. end;
  2326. end;
  2327. SetLength(Result, TJclAddr(P) - TJclAddr(Pointer(Result)) + 1);
  2328. end;
  2329. function ConvertMapFileToJdbgFile(const MapFileName: TFileName): Boolean;
  2330. var
  2331. Dummy1: string;
  2332. Dummy2, Dummy3, Dummy4: Integer;
  2333. begin
  2334. Result := ConvertMapFileToJdbgFile(MapFileName, Dummy1, Dummy2, Dummy3, Dummy4);
  2335. end;
  2336. function ConvertMapFileToJdbgFile(const MapFileName: TFileName; out LinkerBugUnit: string;
  2337. out LineNumberErrors: Integer): Boolean;
  2338. var
  2339. Dummy1, Dummy2: Integer;
  2340. begin
  2341. Result := ConvertMapFileToJdbgFile(MapFileName, LinkerBugUnit, LineNumberErrors,
  2342. Dummy1, Dummy2);
  2343. end;
  2344. function ConvertMapFileToJdbgFile(const MapFileName: TFileName; out LinkerBugUnit: string;
  2345. out LineNumberErrors, MapFileSize, JdbgFileSize: Integer): Boolean;
  2346. var
  2347. JDbgFileName: TFileName;
  2348. Generator: TJclBinDebugGenerator;
  2349. begin
  2350. JDbgFileName := ChangeFileExt(MapFileName, JclDbgFileExtension);
  2351. Generator := TJclBinDebugGenerator.Create(MapFileName, 0);
  2352. try
  2353. MapFileSize := Generator.Stream.Size;
  2354. JdbgFileSize := Generator.DataStream.Size;
  2355. Result := (Generator.DataStream.Size > 0) and Generator.CalculateCheckSum;
  2356. if Result then
  2357. Generator.DataStream.SaveToFile(JDbgFileName);
  2358. LinkerBugUnit := Generator.LinkerBugUnitName;
  2359. LineNumberErrors := Generator.LineNumberErrors;
  2360. finally
  2361. Generator.Free;
  2362. end;
  2363. end;
  2364. function InsertDebugDataIntoExecutableFile(const ExecutableFileName, MapFileName: TFileName;
  2365. out LinkerBugUnit: string; out MapFileSize, JclDebugDataSize: Integer): Boolean;
  2366. var
  2367. Dummy: Integer;
  2368. begin
  2369. Result := InsertDebugDataIntoExecutableFile(ExecutableFileName, MapFileName, LinkerBugUnit,
  2370. MapFileSize, JclDebugDataSize, Dummy);
  2371. end;
  2372. function InsertDebugDataIntoExecutableFile(const ExecutableFileName, MapFileName: TFileName;
  2373. out LinkerBugUnit: string; out MapFileSize, JclDebugDataSize, LineNumberErrors: Integer): Boolean;
  2374. var
  2375. BinDebug: TJclBinDebugGenerator;
  2376. begin
  2377. BinDebug := TJclBinDebugGenerator.Create(MapFileName, 0);
  2378. try
  2379. Result := InsertDebugDataIntoExecutableFile(ExecutableFileName, BinDebug,
  2380. LinkerBugUnit, MapFileSize, JclDebugDataSize, LineNumberErrors);
  2381. finally
  2382. BinDebug.Free;
  2383. end;
  2384. end;
  2385. function InsertDebugDataIntoExecutableFile(const ExecutableFileName: TFileName;
  2386. BinDebug: TJclBinDebugGenerator; out LinkerBugUnit: string;
  2387. out MapFileSize, JclDebugDataSize: Integer): Boolean;
  2388. var
  2389. Dummy: Integer;
  2390. begin
  2391. Result := InsertDebugDataIntoExecutableFile(ExecutableFileName, BinDebug, LinkerBugUnit,
  2392. MapFileSize, JclDebugDataSize, Dummy);
  2393. end;
  2394. function InsertDebugDataIntoExecutableFile(const ExecutableFileName: TFileName;
  2395. BinDebug: TJclBinDebugGenerator; out LinkerBugUnit: string;
  2396. out MapFileSize, JclDebugDataSize, LineNumberErrors: Integer): Boolean;
  2397. var
  2398. ImageStream: TStream;
  2399. NtHeaders32: TImageNtHeaders32;
  2400. NtHeaders64: TImageNtHeaders64;
  2401. ImageSectionHeaders: TImageSectionHeaderArray;
  2402. NtHeadersPosition, ImageSectionHeadersPosition, JclDebugSectionPosition: Int64;
  2403. JclDebugSection: TImageSectionHeader;
  2404. LastSection: PImageSectionHeader;
  2405. VirtualAlignedSize: DWORD;
  2406. NeedFill: Integer;
  2407. procedure RoundUpToAlignment(var Value: DWORD; Alignment: DWORD);
  2408. begin
  2409. if (Value mod Alignment) <> 0 then
  2410. Value := ((Value div Alignment) + 1) * Alignment;
  2411. end;
  2412. procedure MovePointerToRawData(AOffset: DWORD);
  2413. var
  2414. I: Integer;
  2415. begin
  2416. for I := Low(ImageSectionHeaders) to High(ImageSectionHeaders) do
  2417. ImageSectionHeaders[I].PointerToRawData := ImageSectionHeaders[I].PointerToRawData + AOffset;
  2418. end;
  2419. procedure FillZeros(AStream: TStream; ACount: Integer);
  2420. var
  2421. I: Integer;
  2422. X: array[0..511] of Byte;
  2423. begin
  2424. if ACount > 0 then
  2425. begin
  2426. if ACount > Length(X) then
  2427. FillChar(X, SizeOf(X), 0)
  2428. else
  2429. FillChar(X, ACount, 0);
  2430. while ACount > 0 do
  2431. begin
  2432. I := ACount;
  2433. if I > SizeOf(X) then
  2434. I := SizeOf(X);
  2435. AStream.WriteBuffer(X, I);
  2436. Dec(ACount, I);
  2437. end;
  2438. end;
  2439. end;
  2440. procedure WriteSectionHeaders(AStream: TStream; APosition: Integer);
  2441. var
  2442. HeaderSize: Integer;
  2443. begin
  2444. HeaderSize := SizeOf(TImageSectionHeader) * Length(ImageSectionHeaders);
  2445. if (AStream.Seek(APosition, soFromBeginning) <> APosition) or
  2446. (AStream.Write(ImageSectionHeaders[0], HeaderSize) <> HeaderSize) then
  2447. raise EJclPeImageError.CreateRes(@SWriteError);
  2448. FillZeros(AStream, ImageSectionHeaders[0].PointerToRawData - AStream.Position);
  2449. end;
  2450. procedure MoveData(AStream: TStream; AStart, AOffset: Integer);
  2451. var
  2452. CurPos: Integer;
  2453. CurSize: Integer;
  2454. Buffer: array of Byte;
  2455. StartPos: Integer;
  2456. begin
  2457. SetLength(Buffer, 1024 * 1024);
  2458. CurPos := AStream.Size - Length(Buffer);
  2459. StartPos := ImageSectionHeaders[0].PointerToRawData;
  2460. while CurPos > StartPos do
  2461. begin
  2462. if (AStream.Seek(CurPos, soBeginning) <> CurPos) or
  2463. (AStream.Read(Buffer[0], Length(Buffer)) <> Length(Buffer)) then
  2464. raise EJclPeImageError.CreateRes(@SReadError);
  2465. if (AStream.Seek(CurPos + AOffset, soBeginning) <> CurPos + AOffset) or
  2466. (AStream.Write(Buffer[0], Length(Buffer)) <> Length(Buffer)) then
  2467. raise EJclPeImageError.CreateRes(@SWriteError);
  2468. Dec(CurPos, Length(Buffer));
  2469. end;
  2470. CurSize := Length(Buffer) + CurPos - StartPos;
  2471. if (AStream.Seek(StartPos, soBeginning) <> StartPos) or
  2472. (AStream.Read(Buffer[0], CurSize) <> CurSize) then
  2473. raise EJclPeImageError.CreateRes(@SReadError);
  2474. if (AStream.Seek(StartPos + AOffset, soBeginning) <> StartPos + AOffset) or
  2475. (AStream.Write(Buffer[0], CurSize) <> CurSize) then
  2476. raise EJclPeImageError.CreateRes(@SWriteError);
  2477. end;
  2478. procedure CheckHeadersSpace(AStream: TStream);
  2479. begin
  2480. if ImageSectionHeaders[0].PointerToRawData < ImageSectionHeadersPosition +
  2481. (SizeOf(TImageSectionHeader) * (Length(ImageSectionHeaders) + 1)) then
  2482. begin
  2483. MoveData(AStream, ImageSectionHeaders[0].PointerToRawData, NtHeaders64.OptionalHeader.FileAlignment);
  2484. MovePointerToRawData(NtHeaders64.OptionalHeader.FileAlignment);
  2485. WriteSectionHeaders(AStream, ImageSectionHeadersPosition);
  2486. end;
  2487. end;
  2488. begin
  2489. MapFileSize := 0;
  2490. JclDebugDataSize := 0;
  2491. LineNumberErrors := 0;
  2492. LinkerBugUnit := '';
  2493. if BinDebug.Stream <> nil then
  2494. begin
  2495. Result := True;
  2496. if BinDebug.LinkerBug then
  2497. begin
  2498. LinkerBugUnit := BinDebug.LinkerBugUnitName;
  2499. LineNumberErrors := BinDebug.LineNumberErrors;
  2500. end;
  2501. end
  2502. else
  2503. Result := False;
  2504. if not Result then
  2505. Exit;
  2506. ImageStream := TFileStream.Create(ExecutableFileName, fmOpenReadWrite or fmShareExclusive);
  2507. try
  2508. try
  2509. MapFileSize := BinDebug.Stream.Size;
  2510. JclDebugDataSize := BinDebug.DataStream.Size;
  2511. VirtualAlignedSize := JclDebugDataSize;
  2512. // JCLDEBUG
  2513. ResetMemory(JclDebugSection, SizeOf(JclDebugSection));
  2514. // JCLDEBUG Virtual Size
  2515. JclDebugSection.Misc.VirtualSize := JclDebugDataSize;
  2516. // JCLDEBUG Raw data size
  2517. JclDebugSection.SizeOfRawData := JclDebugDataSize;
  2518. // JCLDEBUG Section name
  2519. Move(JclDbgDataResName, JclDebugSection.Name, IMAGE_SIZEOF_SHORT_NAME);
  2520. // JCLDEBUG Characteristics flags
  2521. JclDebugSection.Characteristics := IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;
  2522. case PeMapImgTarget(ImageStream, 0) of
  2523. taWin32:
  2524. begin
  2525. NtHeadersPosition := PeMapImgNtHeaders32(ImageStream, 0, NtHeaders32);
  2526. Assert(NtHeadersPosition <> -1);
  2527. ImageSectionHeadersPosition := PeMapImgSections32(ImageStream, NtHeadersPosition, NtHeaders32, ImageSectionHeaders);
  2528. Assert(ImageSectionHeadersPosition <> -1);
  2529. // Check whether there is not a section with the name already. If so, return True (0000069)
  2530. if PeMapImgFindSection(ImageSectionHeaders, JclDbgDataResName) <> -1 then
  2531. begin
  2532. Result := True;
  2533. Exit;
  2534. end;
  2535. JclDebugSectionPosition := ImageSectionHeadersPosition + (SizeOf(ImageSectionHeaders[0]) * Length(ImageSectionHeaders));
  2536. LastSection := @ImageSectionHeaders[High(ImageSectionHeaders)];
  2537. // Increase the number of sections
  2538. Inc(NtHeaders32.FileHeader.NumberOfSections);
  2539. // JCLDEBUG Virtual Address
  2540. JclDebugSection.VirtualAddress := LastSection^.VirtualAddress + LastSection^.Misc.VirtualSize;
  2541. // JCLDEBUG Physical Offset
  2542. JclDebugSection.PointerToRawData := LastSection^.PointerToRawData + LastSection^.SizeOfRawData;
  2543. // JCLDEBUG section rounding :
  2544. RoundUpToAlignment(JclDebugSection.VirtualAddress, NtHeaders32.OptionalHeader.SectionAlignment);
  2545. RoundUpToAlignment(JclDebugSection.PointerToRawData, NtHeaders32.OptionalHeader.FileAlignment);
  2546. RoundUpToAlignment(JclDebugSection.SizeOfRawData, NtHeaders32.OptionalHeader.FileAlignment);
  2547. // Size of virtual data area
  2548. RoundUpToAlignment(VirtualAlignedSize, NtHeaders32.OptionalHeader.SectionAlignment);
  2549. // Update Size of Image
  2550. Inc(NtHeaders32.OptionalHeader.SizeOfImage, VirtualAlignedSize);
  2551. // Update Initialized data size
  2552. Inc(NtHeaders32.OptionalHeader.SizeOfInitializedData, JclDebugSection.SizeOfRawData);
  2553. // write NT Headers 32
  2554. if (ImageStream.Seek(NtHeadersPosition, soBeginning) <> NtHeadersPosition) or
  2555. (ImageStream.Write(NtHeaders32, SizeOf(NtHeaders32)) <> SizeOf(NtHeaders32)) then
  2556. raise EJclPeImageError.CreateRes(@SWriteError);
  2557. end;
  2558. taWin64:
  2559. begin
  2560. NtHeadersPosition := PeMapImgNtHeaders64(ImageStream, 0, NtHeaders64);
  2561. Assert(NtHeadersPosition <> -1);
  2562. ImageSectionHeadersPosition := PeMapImgSections64(ImageStream, NtHeadersPosition, NtHeaders64, ImageSectionHeaders);
  2563. Assert(ImageSectionHeadersPosition <> -1);
  2564. // Check whether there is not a section with the name already. If so, return True (0000069)
  2565. if PeMapImgFindSection(ImageSectionHeaders, JclDbgDataResName) <> -1 then
  2566. begin
  2567. Result := True;
  2568. Exit;
  2569. end;
  2570. // Check if there is enough space for additional header
  2571. CheckHeadersSpace(ImageStream);
  2572. JclDebugSectionPosition := ImageSectionHeadersPosition + (SizeOf(ImageSectionHeaders[0]) * Length(ImageSectionHeaders));
  2573. LastSection := @ImageSectionHeaders[High(ImageSectionHeaders)];
  2574. // Increase the number of sections
  2575. Inc(NtHeaders64.FileHeader.NumberOfSections);
  2576. // JCLDEBUG Virtual Address
  2577. JclDebugSection.VirtualAddress := LastSection^.VirtualAddress + LastSection^.Misc.VirtualSize;
  2578. // JCLDEBUG Physical Offset
  2579. JclDebugSection.PointerToRawData := LastSection^.PointerToRawData + LastSection^.SizeOfRawData;
  2580. // JCLDEBUG section rounding :
  2581. RoundUpToAlignment(JclDebugSection.VirtualAddress, NtHeaders64.OptionalHeader.SectionAlignment);
  2582. RoundUpToAlignment(JclDebugSection.PointerToRawData, NtHeaders64.OptionalHeader.FileAlignment);
  2583. RoundUpToAlignment(JclDebugSection.SizeOfRawData, NtHeaders64.OptionalHeader.FileAlignment);
  2584. // Size of virtual data area
  2585. RoundUpToAlignment(VirtualAlignedSize, NtHeaders64.OptionalHeader.SectionAlignment);
  2586. // Update Size of Image
  2587. Inc(NtHeaders64.OptionalHeader.SizeOfImage, VirtualAlignedSize);
  2588. // Update Initialized data size
  2589. Inc(NtHeaders64.OptionalHeader.SizeOfInitializedData, JclDebugSection.SizeOfRawData);
  2590. // write NT Headers 64
  2591. if (ImageStream.Seek(NtHeadersPosition, soBeginning) <> NtHeadersPosition) or
  2592. (ImageStream.Write(NtHeaders64, SizeOf(NtHeaders64)) <> SizeOf(NtHeaders64)) then
  2593. raise EJclPeImageError.CreateRes(@SWriteError);
  2594. end;
  2595. else
  2596. Result := False;
  2597. Exit;
  2598. end;
  2599. // write section header
  2600. if (ImageStream.Seek(JclDebugSectionPosition, soBeginning) <> JclDebugSectionPosition) or
  2601. (ImageStream.Write(JclDebugSection, SizeOf(JclDebugSection)) <> SizeOf(JclDebugSection)) then
  2602. raise EJclPeImageError.CreateRes(@SWriteError);
  2603. // Fill data to alignment
  2604. NeedFill := INT_PTR(JclDebugSection.SizeOfRawData) - JclDebugDataSize;
  2605. // Note: Delphi linker seems to generate incorrect (unaligned) size of
  2606. // the executable when adding TD32 debug data so the position could be
  2607. // behind the size of the file then.
  2608. ImageStream.Seek({0 +} JclDebugSection.PointerToRawData, soBeginning);
  2609. ImageStream.CopyFrom(BinDebug.DataStream, 0);
  2610. FillZeros(ImageStream, NeedFill);
  2611. except
  2612. Result := False;
  2613. end;
  2614. finally
  2615. ImageStream.Free;
  2616. end;
  2617. end;
  2618. //=== { TJclBinDebugGenerator } ==============================================
  2619. constructor TJclBinDebugGenerator.Create(const MapFileName: TFileName; Module: HMODULE);
  2620. begin
  2621. inherited Create(MapFileName, Module);
  2622. FDataStream := TMemoryStream.Create;
  2623. FMapFileName := MapFileName;
  2624. if FStream <> nil then
  2625. CreateData;
  2626. end;
  2627. destructor TJclBinDebugGenerator.Destroy;
  2628. begin
  2629. FreeAndNil(FDataStream);
  2630. inherited Destroy;
  2631. end;
  2632. {$OVERFLOWCHECKS OFF}
  2633. function TJclBinDebugGenerator.CalculateCheckSum: Boolean;
  2634. var
  2635. Header: PJclDbgHeader;
  2636. P, EndData: PAnsiChar;
  2637. CheckSum: Integer;
  2638. begin
  2639. Result := DataStream.Size >= SizeOf(TJclDbgHeader);
  2640. if Result then
  2641. begin
  2642. P := DataStream.Memory;
  2643. EndData := P + DataStream.Size;
  2644. Header := PJclDbgHeader(P);
  2645. CheckSum := 0;
  2646. Header^.CheckSum := 0;
  2647. Header^.CheckSumValid := True;
  2648. while P < EndData do
  2649. begin
  2650. Inc(CheckSum, PInteger(P)^);
  2651. Inc(PInteger(P));
  2652. end;
  2653. Header^.CheckSum := CheckSum;
  2654. end;
  2655. end;
  2656. {$IFDEF OVERFLOWCHECKS_ON}
  2657. {$OVERFLOWCHECKS ON}
  2658. {$ENDIF OVERFLOWCHECKS_ON}
  2659. procedure TJclBinDebugGenerator.CreateData;
  2660. var
  2661. {$IFDEF SUPPORTS_GENERICS}
  2662. WordList: TDictionary<string, Integer>;
  2663. {$ELSE}
  2664. WordList: TStringList;
  2665. {$ENDIF SUPPORTS_GENERICS}
  2666. WordStream: TMemoryStream;
  2667. LastSegmentID: Word;
  2668. LastSegmentStored: Boolean;
  2669. function PosLastNameSep(const S: string): Integer;
  2670. var
  2671. InGeneric: Integer;
  2672. begin
  2673. // Unit.Name.ProcName => "Unit.Name" + "ProcName"
  2674. // Unit.Name..ClassName => "UnitName" + ".ClassName"
  2675. // Unit.Name.Class<Unit.Name.OtherClass>.ProcName => "Unit.Name.Class<Unit.Name.OtherClass>" + "ProcName"
  2676. InGeneric := 0;
  2677. for Result := Length(S) downto 1 do
  2678. begin
  2679. case S[Result] of
  2680. '.':
  2681. if InGeneric = 0 then
  2682. if (Result = 1) or (S[Result - 1] <> '.') then
  2683. Exit;
  2684. '>':
  2685. Inc(InGeneric);
  2686. '<':
  2687. Dec(InGeneric);
  2688. end;
  2689. end;
  2690. Result := 0;
  2691. end;
  2692. function AddWord(const S: string): Integer;
  2693. var
  2694. {$IFDEF SUPPORTS_GENERICS}
  2695. LowerS: string;
  2696. {$ELSE}
  2697. N: Integer;
  2698. {$ENDIF SUPPORTS_GENERICS}
  2699. E: AnsiString;
  2700. begin
  2701. if S = '' then
  2702. begin
  2703. Result := 0;
  2704. Exit;
  2705. end;
  2706. {$IFDEF SUPPORTS_GENERICS}
  2707. LowerS := AnsiLowerCase(S);
  2708. if not WordList.TryGetValue(LowerS, Result) then
  2709. begin
  2710. Result := WordStream.Position;
  2711. E := EncodeNameString(S);
  2712. WordStream.Write(E[1], Length(E));
  2713. WordList.Add(LowerS, Result);
  2714. end;
  2715. {$ELSE} // for large map files this is very slow
  2716. N := WordList.IndexOf(S);
  2717. if N = -1 then
  2718. begin
  2719. Result := WordStream.Position;
  2720. E := EncodeNameString(S);
  2721. WordStream.Write(E[1], Length(E));
  2722. WordList.AddObject(S, TObject(Result));
  2723. end
  2724. else
  2725. Result := DWORD(WordList.Objects[N]);
  2726. {$ENDIF SUPPORTS_GENERICS}
  2727. Inc(Result);
  2728. end;
  2729. procedure WriteValue(Value: Integer);
  2730. var
  2731. L: Integer;
  2732. D: DWORD;
  2733. P: array [1..5] of Byte;
  2734. begin
  2735. D := Value and $FFFFFFFF;
  2736. L := 0;
  2737. while D > $7F do
  2738. begin
  2739. Inc(L);
  2740. P[L] := (D and $7F) or $80;
  2741. D := D shr 7;
  2742. end;
  2743. Inc(L);
  2744. P[L] := (D and $7F);
  2745. FDataStream.Write(P, L);
  2746. end;
  2747. procedure WriteValueOfs(Value: Integer; var LastValue: Integer);
  2748. begin
  2749. WriteValue(Value - LastValue);
  2750. LastValue := Value;
  2751. end;
  2752. function IsSegmentStored(SegID: Word): Boolean;
  2753. var
  2754. SegIndex: Integer;
  2755. GroupName: string;
  2756. begin
  2757. if SegID <> LastSegmentID then
  2758. begin
  2759. LastSegmentID := $FFFF;
  2760. LastSegmentStored := False;
  2761. for SegIndex := Low(FSegmentClasses) to High(FSegmentClasses) do
  2762. if FSegmentClasses[SegIndex].Segment = SegID then
  2763. begin
  2764. LastSegmentID := FSegmentClasses[SegIndex].Segment;
  2765. GroupName := MapStringCacheToStr(FSegmentClasses[SegIndex].GroupName);
  2766. LastSegmentStored := (GroupName = 'CODE') or (GroupName = 'ICODE');
  2767. Break;
  2768. end;
  2769. end;
  2770. Result := LastSegmentStored;
  2771. end;
  2772. const
  2773. AlignBytes: array[0..2] of Byte = (0, 0, 0);
  2774. var
  2775. FileHeader: TJclDbgHeader;
  2776. I, D: Integer;
  2777. S: string;
  2778. L1, L2, L3: Integer;
  2779. FirstWord, SecondWord: Integer;
  2780. WordStreamSize, DataStreamSize: Int64;
  2781. begin
  2782. LastSegmentID := $FFFF;
  2783. WordStream := TMemoryStream.Create;
  2784. {$IFDEF SUPPORTS_GENERICS}
  2785. WordList := TDictionary<string, Integer>.Create(Length(FSourceNames) + Length(FProcNames));
  2786. {$ELSE}
  2787. WordList := TStringList.Create;
  2788. {$ENDIF SUPPORTS_GENERICS}
  2789. try
  2790. {$IFNDEF SUPPORTS_GENERICS}
  2791. WordList.Sorted := True;
  2792. WordList.Duplicates := dupError;
  2793. {$ENDIF ~SUPPORTS_GENERICS}
  2794. WordStream.SetSize((Length(FSourceNames) + Length(FProcNames)) * 40); // take an average of 40 chars per identifier
  2795. FileHeader.Signature := JclDbgDataSignature;
  2796. FileHeader.Version := JclDbgHeaderVersion;
  2797. FileHeader.CheckSum := 0;
  2798. FileHeader.CheckSumValid := False;
  2799. FileHeader.ModuleName := AddWord(PathExtractFileNameNoExt(FMapFileName));
  2800. FDataStream.WriteBuffer(FileHeader, SizeOf(FileHeader));
  2801. FileHeader.Units := FDataStream.Position;
  2802. L1 := 0;
  2803. L2 := 0;
  2804. for I := 0 to Length(FSegments) - 1 do
  2805. if IsSegmentStored(FSegments[I].Segment) then
  2806. begin
  2807. WriteValueOfs(FSegments[I].StartVA, L1);
  2808. WriteValueOfs(AddWord(MapStringCacheToModuleName(FSegments[I].UnitName)), L2);
  2809. end;
  2810. WriteValue(MaxInt);
  2811. FileHeader.SourceNames := FDataStream.Position;
  2812. L1 := 0;
  2813. L2 := 0;
  2814. for I := 0 to Length(FSourceNames) - 1 do
  2815. if IsSegmentStored(FSourceNames[I].Segment) then
  2816. begin
  2817. // FSourceNames[] is sorted by VA, so if the source file name is the same as the previous
  2818. // we don't need to store it because the VA will be matched by the previous entry.
  2819. // This removes a lot of "Generics.Collections.pas" entries.
  2820. S := MapStringCacheToStr(FSourceNames[I].ProcName);
  2821. if (I = 0) or (FSourceNames[I - 1].ProcName.CachedValue <> S) then
  2822. begin
  2823. WriteValueOfs(FSourceNames[I].VA, L1);
  2824. WriteValueOfs(AddWord(S), L2);
  2825. end;
  2826. end;
  2827. WriteValue(MaxInt);
  2828. FileHeader.Symbols := FDataStream.Position;
  2829. L1 := 0;
  2830. L2 := 0;
  2831. L3 := 0;
  2832. for I := 0 to Length(FProcNames) - 1 do
  2833. if IsSegmentStored(FProcNames[I].Segment) then
  2834. begin
  2835. WriteValueOfs(FProcNames[I].VA, L1);
  2836. // MAP files generated by C++Builder have spaces in their names
  2837. S := MapStringCacheToStr(FProcNames[I].ProcName, True);
  2838. D := PosLastNameSep(S);
  2839. if D = 1 then
  2840. begin
  2841. FirstWord := 0;
  2842. SecondWord := 0;
  2843. end
  2844. else
  2845. if D = 0 then
  2846. begin
  2847. FirstWord := AddWord(S);
  2848. SecondWord := 0;
  2849. end
  2850. else
  2851. begin
  2852. FirstWord := AddWord(Copy(S, 1, D - 1));
  2853. SecondWord := AddWord(Copy(S, D + 1, Length(S)));
  2854. end;
  2855. WriteValueOfs(FirstWord, L2);
  2856. WriteValueOfs(SecondWord, L3);
  2857. end;
  2858. WriteValue(MaxInt);
  2859. FileHeader.LineNumbers := FDataStream.Position;
  2860. L1 := 0;
  2861. L2 := 0;
  2862. for I := 0 to Length(FLineNumbers) - 1 do
  2863. if IsSegmentStored(FLineNumbers[I].Segment) then
  2864. begin
  2865. WriteValueOfs(FLineNumbers[I].VA, L1);
  2866. WriteValueOfs(FLineNumbers[I].LineNumber, L2);
  2867. end;
  2868. WriteValue(MaxInt);
  2869. FileHeader.Words := FDataStream.Position;
  2870. // Calculate and allocate the required size in advance instead of reallocating on the fly.
  2871. WordStreamSize := WordStream.Position;
  2872. DataStreamSize := FDataStream.Position + WordStreamSize;
  2873. DataStreamSize := DataStreamSize + (4 - (DataStreamSize and $3));
  2874. FDataStream.Size := DataStreamSize; // set capacity
  2875. WordStream.Position := 0;
  2876. FDataStream.CopyFrom(WordStream, WordStreamSize);
  2877. // Align to 4 bytes
  2878. FDataStream.WriteBuffer(AlignBytes, 4 - (FDataStream.Position and $3));
  2879. if FDataStream.Size <> FDataStream.Position then // just in case something changed without adjusting the size calculation
  2880. FDataStream.Size := FDataStream.Position;
  2881. // Update the file header
  2882. FDataStream.Seek(0, soBeginning);
  2883. FDataStream.WriteBuffer(FileHeader, SizeOf(FileHeader));
  2884. finally
  2885. WordStream.Free;
  2886. WordList.Free;
  2887. end;
  2888. end;
  2889. //=== { TJclBinDebugScanner } ================================================
  2890. constructor TJclBinDebugScanner.Create(AStream: TCustomMemoryStream; CacheData, CacheProcNames: Boolean);
  2891. begin
  2892. inherited Create;
  2893. FCacheData := CacheData;
  2894. FCacheProcNames := CacheProcNames;
  2895. FStream := AStream;
  2896. CheckFormat;
  2897. end;
  2898. procedure TJclBinDebugScanner.CacheLineNumbers;
  2899. var
  2900. P: Pointer;
  2901. Value, LineNumber, C, Ln: Integer;
  2902. CurrVA: DWORD;
  2903. begin
  2904. if FLineNumbers = nil then
  2905. begin
  2906. LineNumber := 0;
  2907. CurrVA := 0;
  2908. C := 0;
  2909. Ln := 0;
  2910. P := MakePtr(PJclDbgHeader(FStream.Memory)^.LineNumbers);
  2911. Value := 0;
  2912. while ReadValue(P, Value) do
  2913. begin
  2914. Inc(CurrVA, Value);
  2915. ReadValue(P, Value);
  2916. Inc(LineNumber, Value);
  2917. if C = Ln then
  2918. begin
  2919. if Ln < 64 then
  2920. Ln := 64
  2921. else
  2922. Ln := Ln + Ln div 4;
  2923. SetLength(FLineNumbers, Ln);
  2924. end;
  2925. FLineNumbers[C].VA := CurrVA;
  2926. FLineNumbers[C].LineNumber := LineNumber;
  2927. Inc(C);
  2928. end;
  2929. SetLength(FLineNumbers, C);
  2930. end;
  2931. end;
  2932. procedure TJclBinDebugScanner.CacheProcNames;
  2933. var
  2934. P: Pointer;
  2935. Value, FirstWord, SecondWord, C, Ln: Integer;
  2936. CurrAddr: DWORD;
  2937. begin
  2938. if FProcNames = nil then
  2939. begin
  2940. FirstWord := 0;
  2941. SecondWord := 0;
  2942. CurrAddr := 0;
  2943. C := 0;
  2944. Ln := 0;
  2945. P := MakePtr(PJclDbgHeader(FStream.Memory)^.Symbols);
  2946. Value := 0;
  2947. while ReadValue(P, Value) do
  2948. begin
  2949. Inc(CurrAddr, Value);
  2950. ReadValue(P, Value);
  2951. Inc(FirstWord, Value);
  2952. ReadValue(P, Value);
  2953. Inc(SecondWord, Value);
  2954. if C = Ln then
  2955. begin
  2956. if Ln < 64 then
  2957. Ln := 64
  2958. else
  2959. Ln := Ln + Ln div 4;
  2960. SetLength(FProcNames, Ln);
  2961. end;
  2962. FProcNames[C].Addr := CurrAddr;
  2963. FProcNames[C].FirstWord := FirstWord;
  2964. FProcNames[C].SecondWord := SecondWord;
  2965. if FCacheProcNames then
  2966. begin
  2967. if (FirstWord <> 0) and (SecondWord <> 0) then
  2968. FProcNames[C].Text := DataToStr(FirstWord) + '.' + DataToStr(SecondWord)
  2969. else if FirstWord <> 0 then
  2970. FProcNames[C].Text := DataToStr(FirstWord)
  2971. else
  2972. FProcNames[C].Text := '';
  2973. end
  2974. else
  2975. FProcNames[C].Text := '';
  2976. Inc(C);
  2977. end;
  2978. SetLength(FProcNames, C);
  2979. end;
  2980. end;
  2981. {$OVERFLOWCHECKS OFF}
  2982. procedure TJclBinDebugScanner.CheckFormat;
  2983. var
  2984. CheckSum: Integer;
  2985. Data, EndData: PAnsiChar;
  2986. Header: PJclDbgHeader;
  2987. begin
  2988. Data := FStream.Memory;
  2989. Header := PJclDbgHeader(Data);
  2990. FValidFormat := (Data <> nil) and (FStream.Size > SizeOf(TJclDbgHeader)) and
  2991. (FStream.Size mod 4 = 0) and
  2992. (Header^.Signature = JclDbgDataSignature) and (Header^.Version = JclDbgHeaderVersion);
  2993. if FValidFormat and Header^.CheckSumValid then
  2994. begin
  2995. CheckSum := -Header^.CheckSum;
  2996. EndData := Data + FStream.Size;
  2997. while Data < EndData do
  2998. begin
  2999. Inc(CheckSum, PInteger(Data)^);
  3000. Inc(PInteger(Data));
  3001. end;
  3002. CheckSum := (CheckSum shr 8) or (CheckSum shl 24);
  3003. FValidFormat := (CheckSum = Header^.CheckSum);
  3004. end;
  3005. end;
  3006. {$IFDEF OVERFLOWCHECKS_ON}
  3007. {$OVERFLOWCHECKS ON}
  3008. {$ENDIF OVERFLOWCHECKS_ON}
  3009. function TJclBinDebugScanner.DataToStr(A: Integer): string;
  3010. var
  3011. P: PAnsiChar;
  3012. begin
  3013. if A = 0 then
  3014. Result := ''
  3015. else
  3016. begin
  3017. P := PAnsiChar(TJclAddr(FStream.Memory) + TJclAddr(A) + TJclAddr(PJclDbgHeader(FStream.Memory)^.Words) - 1);
  3018. Result := DecodeNameString(P);
  3019. end;
  3020. end;
  3021. function TJclBinDebugScanner.GetModuleName: string;
  3022. begin
  3023. Result := DataToStr(PJclDbgHeader(FStream.Memory)^.ModuleName);
  3024. end;
  3025. function TJclBinDebugScanner.IsModuleNameValid(const Name: TFileName): Boolean;
  3026. begin
  3027. Result := AnsiSameText(ModuleName, PathExtractFileNameNoExt(Name));
  3028. end;
  3029. function TJclBinDebugScanner.LineNumberFromAddr(Addr: DWORD): Integer;
  3030. var
  3031. Dummy: Integer;
  3032. begin
  3033. Result := LineNumberFromAddr(Addr, Dummy);
  3034. end;
  3035. function TJclBinDebugScanner.LineNumberFromAddr(Addr: DWORD; out Offset: Integer): Integer;
  3036. var
  3037. P: Pointer;
  3038. Value, LineNumber: Integer;
  3039. CurrVA, ModuleStartVA, ItemVA: DWORD;
  3040. begin
  3041. ModuleStartVA := ModuleStartFromAddr(Addr);
  3042. LineNumber := 0;
  3043. Offset := 0;
  3044. if FCacheData then
  3045. begin
  3046. CacheLineNumbers;
  3047. for Value := Length(FLineNumbers) - 1 downto 0 do
  3048. if FLineNumbers[Value].VA <= Addr then
  3049. begin
  3050. if FLineNumbers[Value].VA >= ModuleStartVA then
  3051. begin
  3052. LineNumber := FLineNumbers[Value].LineNumber;
  3053. Offset := Addr - FLineNumbers[Value].VA;
  3054. end;
  3055. Break;
  3056. end;
  3057. end
  3058. else
  3059. begin
  3060. P := MakePtr(PJclDbgHeader(FStream.Memory)^.LineNumbers);
  3061. CurrVA := 0;
  3062. ItemVA := 0;
  3063. while ReadValue(P, Value) do
  3064. begin
  3065. Inc(CurrVA, Value);
  3066. if Addr < CurrVA then
  3067. begin
  3068. if ItemVA < ModuleStartVA then
  3069. begin
  3070. LineNumber := 0;
  3071. Offset := 0;
  3072. end;
  3073. Break;
  3074. end
  3075. else
  3076. begin
  3077. ItemVA := CurrVA;
  3078. ReadValue(P, Value);
  3079. Inc(LineNumber, Value);
  3080. Offset := Addr - CurrVA;
  3081. end;
  3082. end;
  3083. end;
  3084. Result := LineNumber;
  3085. end;
  3086. function TJclBinDebugScanner.MakePtr(A: Integer): Pointer;
  3087. begin
  3088. Result := Pointer(TJclAddr(FStream.Memory) + TJclAddr(A));
  3089. end;
  3090. function TJclBinDebugScanner.ModuleNameFromAddr(Addr: DWORD): string;
  3091. var
  3092. Value, Name: Integer;
  3093. StartAddr: DWORD;
  3094. P: Pointer;
  3095. begin
  3096. P := MakePtr(PJclDbgHeader(FStream.Memory)^.Units);
  3097. Name := 0;
  3098. StartAddr := 0;
  3099. Value := 0;
  3100. while ReadValue(P, Value) do
  3101. begin
  3102. Inc(StartAddr, Value);
  3103. if Addr < StartAddr then
  3104. Break
  3105. else
  3106. begin
  3107. ReadValue(P, Value);
  3108. Inc(Name, Value);
  3109. end;
  3110. end;
  3111. Result := DataToStr(Name);
  3112. end;
  3113. function TJclBinDebugScanner.ModuleStartFromAddr(Addr: DWORD): DWORD;
  3114. var
  3115. Value: Integer;
  3116. StartAddr, ModuleStartAddr: DWORD;
  3117. P: Pointer;
  3118. begin
  3119. P := MakePtr(PJclDbgHeader(FStream.Memory)^.Units);
  3120. StartAddr := 0;
  3121. ModuleStartAddr := DWORD(-1);
  3122. Value := 0;
  3123. while ReadValue(P, Value) do
  3124. begin
  3125. Inc(StartAddr, Value);
  3126. if Addr < StartAddr then
  3127. Break
  3128. else
  3129. begin
  3130. ReadValue(P, Value);
  3131. ModuleStartAddr := StartAddr;
  3132. end;
  3133. end;
  3134. Result := ModuleStartAddr;
  3135. end;
  3136. function TJclBinDebugScanner.ProcNameFromAddr(Addr: DWORD): string;
  3137. var
  3138. Dummy: Integer;
  3139. begin
  3140. Result := ProcNameFromAddr(Addr, Dummy);
  3141. end;
  3142. function TJclBinDebugScanner.ProcNameFromAddr(Addr: DWORD; out Offset: Integer): string;
  3143. var
  3144. P: Pointer;
  3145. Value, FirstWord, SecondWord: Integer;
  3146. CurrAddr, ModuleStartAddr, ItemAddr: DWORD;
  3147. begin
  3148. ModuleStartAddr := ModuleStartFromAddr(Addr);
  3149. FirstWord := 0;
  3150. SecondWord := 0;
  3151. Offset := 0;
  3152. if FCacheData then
  3153. begin
  3154. CacheProcNames;
  3155. for Value := Length(FProcNames) - 1 downto 0 do
  3156. if FProcNames[Value].Addr <= Addr then
  3157. begin
  3158. if FProcNames[Value].Addr >= ModuleStartAddr then
  3159. begin
  3160. FirstWord := FProcNames[Value].FirstWord;
  3161. SecondWord := FProcNames[Value].SecondWord;
  3162. Offset := Addr - FProcNames[Value].Addr;
  3163. end;
  3164. Break;
  3165. end;
  3166. end
  3167. else
  3168. begin
  3169. P := MakePtr(PJclDbgHeader(FStream.Memory)^.Symbols);
  3170. CurrAddr := 0;
  3171. ItemAddr := 0;
  3172. while ReadValue(P, Value) do
  3173. begin
  3174. Inc(CurrAddr, Value);
  3175. if Addr < CurrAddr then
  3176. begin
  3177. if ItemAddr < ModuleStartAddr then
  3178. begin
  3179. FirstWord := 0;
  3180. SecondWord := 0;
  3181. Offset := 0;
  3182. end;
  3183. Break;
  3184. end
  3185. else
  3186. begin
  3187. ItemAddr := CurrAddr;
  3188. ReadValue(P, Value);
  3189. Inc(FirstWord, Value);
  3190. ReadValue(P, Value);
  3191. Inc(SecondWord, Value);
  3192. Offset := Addr - CurrAddr;
  3193. end;
  3194. end;
  3195. end;
  3196. if FirstWord <> 0 then
  3197. begin
  3198. Result := DataToStr(FirstWord);
  3199. if SecondWord <> 0 then
  3200. Result := Result + '.' + DataToStr(SecondWord);
  3201. end
  3202. else
  3203. Result := '';
  3204. end;
  3205. class function TJclBinDebugScanner.ReadValue(var P: Pointer; var Value: Integer): Boolean;
  3206. var
  3207. N: Integer;
  3208. I: Integer;
  3209. B: Byte;
  3210. begin
  3211. N := 0;
  3212. I := 0;
  3213. repeat
  3214. B := PByte(P)^;
  3215. Inc(PByte(P));
  3216. Inc(N, (B and $7F) shl I);
  3217. Inc(I, 7);
  3218. until B and $80 = 0;
  3219. Value := N;
  3220. Result := (N <> MaxInt);
  3221. end;
  3222. function TJclBinDebugScanner.SourceNameFromAddr(Addr: DWORD): string;
  3223. var
  3224. Value, Name: Integer;
  3225. StartAddr, ModuleStartAddr, ItemAddr: DWORD;
  3226. P: Pointer;
  3227. Found: Boolean;
  3228. begin
  3229. ModuleStartAddr := ModuleStartFromAddr(Addr);
  3230. P := MakePtr(PJclDbgHeader(FStream.Memory)^.SourceNames);
  3231. Name := 0;
  3232. StartAddr := 0;
  3233. ItemAddr := 0;
  3234. Found := False;
  3235. Value := 0;
  3236. while ReadValue(P, Value) do
  3237. begin
  3238. Inc(StartAddr, Value);
  3239. if Addr < StartAddr then
  3240. begin
  3241. if ItemAddr < ModuleStartAddr then
  3242. Name := 0
  3243. else
  3244. Found := True;
  3245. Break;
  3246. end
  3247. else
  3248. begin
  3249. ItemAddr := StartAddr;
  3250. ReadValue(P, Value);
  3251. Inc(Name, Value);
  3252. end;
  3253. end;
  3254. if Found then
  3255. Result := DataToStr(Name)
  3256. else
  3257. Result := '';
  3258. end;
  3259. function TJclBinDebugScanner.VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;
  3260. var
  3261. P: Pointer;
  3262. VA: DWORD;
  3263. I, Value: Integer;
  3264. FirstWord, SecondWord: Integer;
  3265. QualifiedName, S: string;
  3266. begin
  3267. Result := 0;
  3268. if (UnitName = '') or (ProcName = '') then
  3269. Exit;
  3270. QualifiedName := UnitName + '.' + ProcName;
  3271. if FCacheData then
  3272. begin
  3273. CacheProcNames;
  3274. for I := Low(FProcNames) to High(FProcNames) do
  3275. begin
  3276. if FProcNames[I].Text <> '' then
  3277. S := FProcNames[I].Text
  3278. else
  3279. begin
  3280. if FProcNames[I].FirstWord = 0 then
  3281. Continue;
  3282. if (FProcNames[I].FirstWord <> 0) and (FProcNames[I].SecondWord <> 0) then
  3283. FProcNames[I].Text := DataToStr(FProcNames[I].FirstWord ) + '.' + DataToStr(FProcNames[I].SecondWord)
  3284. else if FProcNames[I].FirstWord <> 0 then
  3285. FProcNames[I].Text := DataToStr(FProcNames[I].FirstWord)
  3286. else
  3287. FProcNames[I].Text := '';
  3288. end;
  3289. if CompareText(FProcNames[I].Text, QualifiedName) = 0 then
  3290. begin
  3291. Result := FProcNames[i].Addr;
  3292. Break;
  3293. end;
  3294. end;
  3295. end
  3296. else
  3297. begin
  3298. P := MakePtr(PJclDbgHeader(FStream.Memory)^.Symbols);
  3299. VA := 0;
  3300. FirstWord := 0;
  3301. SecondWord := 0;
  3302. while ReadValue(P, Value) do
  3303. begin
  3304. Inc(VA, Value);
  3305. ReadValue(P, Value);
  3306. Inc(FirstWord, Value);
  3307. ReadValue(P, Value);
  3308. Inc(SecondWord, Value);
  3309. if FirstWord = 0 then
  3310. Continue;
  3311. S := DataToStr(FirstWord);
  3312. if SecondWord <> 0 then
  3313. S := S + '.' + DataToStr(SecondWord);
  3314. if CompareText(S, QualifiedName) = 0 then
  3315. begin
  3316. Result := VA;
  3317. Break;
  3318. end;
  3319. end;
  3320. end;
  3321. end;
  3322. //=== { TJclLocationInfoEx } =================================================
  3323. constructor TJclLocationInfoEx.Create(AParent: TJclCustomLocationInfoList; Address: Pointer);
  3324. var
  3325. Options: TJclLocationInfoListOptions;
  3326. begin
  3327. inherited Create;
  3328. FAddress := Address;
  3329. FParent := AParent;
  3330. if Assigned(FParent) then
  3331. Options := FParent.Options
  3332. else
  3333. Options := [];
  3334. Fill(Options);
  3335. end;
  3336. procedure TJclLocationInfoEx.AssignTo(Dest: TPersistent);
  3337. begin
  3338. if Dest is TJclLocationInfoEx then
  3339. begin
  3340. TJclLocationInfoEx(Dest).FAddress := FAddress;
  3341. TJclLocationInfoEx(Dest).FBinaryFileName := FBinaryFileName;
  3342. TJclLocationInfoEx(Dest).FDebugInfo := FDebugInfo;
  3343. TJclLocationInfoEx(Dest).FLineNumber := FLineNumber;
  3344. TJclLocationInfoEx(Dest).FLineNumberOffsetFromProcedureStart := FLineNumberOffsetFromProcedureStart;
  3345. TJclLocationInfoEx(Dest).FModuleName := FModuleName;
  3346. TJclLocationInfoEx(Dest).FOffsetFromLineNumber := FOffsetFromLineNumber;
  3347. TJclLocationInfoEx(Dest).FOffsetFromProcName := FOffsetFromProcName;
  3348. TJclLocationInfoEx(Dest).FProcedureName := FProcedureName;
  3349. TJclLocationInfoEx(Dest).FSourceName := FSourceName;
  3350. TJclLocationInfoEx(Dest).FSourceUnitName := FSourceUnitName;
  3351. TJclLocationInfoEx(Dest).FUnitVersionDateTime := FUnitVersionDateTime;
  3352. TJclLocationInfoEx(Dest).FUnitVersionExtra := FUnitVersionExtra;
  3353. TJclLocationInfoEx(Dest).FUnitVersionLogPath := FUnitVersionLogPath;
  3354. TJclLocationInfoEx(Dest).FUnitVersionRCSfile := FUnitVersionRCSfile;
  3355. TJclLocationInfoEx(Dest).FUnitVersionRevision := FUnitVersionRevision;
  3356. TJclLocationInfoEx(Dest).FVAddress := FVAddress;
  3357. TJclLocationInfoEx(Dest).FValues := FValues;
  3358. end
  3359. else
  3360. inherited AssignTo(Dest);
  3361. end;
  3362. procedure TJclLocationInfoEx.Clear;
  3363. begin
  3364. FAddress := nil;
  3365. Fill([]);
  3366. end;
  3367. procedure TJclLocationInfoEx.Fill(AOptions: TJclLocationInfoListOptions);
  3368. var
  3369. Info, StartProcInfo: TJclLocationInfo;
  3370. FixedProcedureName: string;
  3371. Module: HMODULE;
  3372. {$IFDEF UNITVERSIONING}
  3373. I: Integer;
  3374. UnitVersion: TUnitVersion;
  3375. UnitVersioning: TUnitVersioning;
  3376. UnitVersioningModule: TUnitVersioningModule;
  3377. {$ENDIF UNITVERSIONING}
  3378. begin
  3379. FValues := [];
  3380. if liloAutoGetAddressInfo in AOptions then
  3381. begin
  3382. Module := ModuleFromAddr(FAddress);
  3383. FVAddress := Pointer(TJclAddr(FAddress) - TJclAddr(Module) - ModuleCodeOffset);
  3384. FModuleName := ExtractFileName(GetModulePath(Module));
  3385. end
  3386. else
  3387. begin
  3388. {$IFDEF UNITVERSIONING}
  3389. Module := 0;
  3390. {$ENDIF UNITVERSIONING}
  3391. FVAddress := nil;
  3392. FModuleName := '';
  3393. end;
  3394. if (liloAutoGetLocationInfo in AOptions) and GetLocationInfo(FAddress, Info) then
  3395. begin
  3396. FValues := FValues + [lievLocationInfo];
  3397. FOffsetFromProcName := Info.OffsetFromProcName;
  3398. FSourceUnitName := Info.UnitName;
  3399. FixedProcedureName := Info.ProcedureName;
  3400. if Pos(Info.UnitName + '.', FixedProcedureName) = 1 then
  3401. FixedProcedureName := Copy(FixedProcedureName, Length(Info.UnitName) + 2, Length(FixedProcedureName) - Length(Info.UnitName) - 1);
  3402. FProcedureName := FixedProcedureName;
  3403. FSourceName := Info.SourceName;
  3404. FLineNumber := Info.LineNumber;
  3405. if FLineNumber > 0 then
  3406. FOffsetFromLineNumber := Info.OffsetFromLineNumber
  3407. else
  3408. FOffsetFromLineNumber := 0;
  3409. if GetLocationInfo(Pointer(TJclAddr(Info.Address) -
  3410. Cardinal(Info.OffsetFromProcName)), StartProcInfo) and (StartProcInfo.LineNumber > 0) then
  3411. begin
  3412. FLineNumberOffsetFromProcedureStart := Info.LineNumber - StartProcInfo.LineNumber;
  3413. FValues := FValues + [lievProcedureStartLocationInfo];
  3414. end
  3415. else
  3416. FLineNumberOffsetFromProcedureStart := 0;
  3417. FDebugInfo := Info.DebugInfo;
  3418. FBinaryFileName := Info.BinaryFileName;
  3419. end
  3420. else
  3421. begin
  3422. FOffsetFromProcName := 0;
  3423. FSourceUnitName := '';
  3424. FProcedureName := '';
  3425. FSourceName := '';
  3426. FLineNumber := 0;
  3427. FOffsetFromLineNumber := 0;
  3428. FLineNumberOffsetFromProcedureStart := 0;
  3429. FDebugInfo := nil;
  3430. FBinaryFileName := '';
  3431. end;
  3432. FUnitVersionDateTime := 0;
  3433. FUnitVersionLogPath := '';
  3434. FUnitVersionRCSfile := '';
  3435. FUnitVersionRevision := '';
  3436. {$IFDEF UNITVERSIONING}
  3437. if (liloAutoGetUnitVersionInfo in AOptions) and (FSourceName <> '') then
  3438. begin
  3439. if not (liloAutoGetAddressInfo in AOptions) then
  3440. Module := ModuleFromAddr(FAddress);
  3441. UnitVersioning := GetUnitVersioning;
  3442. for I := 0 to UnitVersioning.ModuleCount - 1 do
  3443. begin
  3444. UnitVersioningModule := UnitVersioning.Modules[I];
  3445. if UnitVersioningModule.Instance = Module then
  3446. begin
  3447. UnitVersion := UnitVersioningModule.FindUnit(FSourceName);
  3448. if Assigned(UnitVersion) then
  3449. begin
  3450. FUnitVersionDateTime := UnitVersion.DateTime;
  3451. FUnitVersionLogPath := UnitVersion.LogPath;
  3452. FUnitVersionRCSfile := UnitVersion.RCSfile;
  3453. FUnitVersionRevision := UnitVersion.Revision;
  3454. FValues := FValues + [lievUnitVersionInfo];
  3455. Break;
  3456. end;
  3457. end;
  3458. if lievUnitVersionInfo in FValues then
  3459. Break;
  3460. end;
  3461. end;
  3462. {$ENDIF UNITVERSIONING}
  3463. end;
  3464. { TODO -oUSc : Include... better as function than property? }
  3465. function TJclLocationInfoEx.GetAsString: string;
  3466. const
  3467. IncludeStartProcLineOffset = True;
  3468. IncludeAddressOffset = True;
  3469. IncludeModuleName = True;
  3470. var
  3471. IncludeVAddress: Boolean;
  3472. OffsetStr, StartProcOffsetStr: string;
  3473. begin
  3474. IncludeVAddress := True;
  3475. OffsetStr := '';
  3476. if lievLocationInfo in FValues then
  3477. begin
  3478. if LineNumber > 0 then
  3479. begin
  3480. if IncludeStartProcLineOffset and (lievProcedureStartLocationInfo in FValues) then
  3481. StartProcOffsetStr := Format(' + %d', [LineNumberOffsetFromProcedureStart])
  3482. else
  3483. StartProcOffsetStr := '';
  3484. if IncludeAddressOffset then
  3485. begin
  3486. if OffsetFromLineNumber >= 0 then
  3487. OffsetStr := Format(' + $%x', [OffsetFromLineNumber])
  3488. else
  3489. OffsetStr := Format(' - $%x', [-OffsetFromLineNumber])
  3490. end;
  3491. Result := Format('[%p] %s.%s (Line %u, "%s"%s)%s', [Address, SourceUnitName, ProcedureName, LineNumber,
  3492. SourceName, StartProcOffsetStr, OffsetStr]);
  3493. end
  3494. else
  3495. begin
  3496. if IncludeAddressOffset then
  3497. OffsetStr := Format(' + $%x', [OffsetFromProcName]);
  3498. if SourceUnitName <> '' then
  3499. Result := Format('[%p] %s.%s%s', [Address, SourceUnitName, ProcedureName, OffsetStr])
  3500. else
  3501. Result := Format('[%p] %s%s', [Address, ProcedureName, OffsetStr]);
  3502. end;
  3503. end
  3504. else
  3505. begin
  3506. Result := Format('[%p]', [Address]);
  3507. IncludeVAddress := True;
  3508. end;
  3509. if IncludeVAddress or IncludeModuleName then
  3510. begin
  3511. if IncludeVAddress then
  3512. begin
  3513. OffsetStr := Format('(%p) ', [VAddress]);
  3514. Result := OffsetStr + Result;
  3515. end;
  3516. if IncludeModuleName then
  3517. Insert(Format('{%-12s}', [ModuleName]), Result, 11 {$IFDEF CPUX64}+ 8{$ENDIF});
  3518. end;
  3519. end;
  3520. //=== { TJclCustomLocationInfoList } =========================================
  3521. constructor TJclCustomLocationInfoList.Create;
  3522. begin
  3523. inherited Create;
  3524. FItemClass := TJclLocationInfoEx;
  3525. FItems := TObjectList.Create;
  3526. FOptions := [];
  3527. end;
  3528. destructor TJclCustomLocationInfoList.Destroy;
  3529. begin
  3530. FItems.Free;
  3531. inherited Destroy;
  3532. end;
  3533. procedure TJclCustomLocationInfoList.AddStackInfoList(AStackInfoList: TObject);
  3534. var
  3535. I: Integer;
  3536. begin
  3537. TJclStackInfoList(AStackInfoList).ForceStackTracing;
  3538. for I := 0 to TJclStackInfoList(AStackInfoList).Count - 1 do
  3539. InternalAdd(TJclStackInfoList(AStackInfoList)[I].CallerAddr);
  3540. end;
  3541. procedure TJclCustomLocationInfoList.AssignTo(Dest: TPersistent);
  3542. var
  3543. I: Integer;
  3544. begin
  3545. if Dest is TJclCustomLocationInfoList then
  3546. begin
  3547. TJclCustomLocationInfoList(Dest).Clear;
  3548. for I := 0 to Count - 1 do
  3549. TJclCustomLocationInfoList(Dest).InternalAdd(nil).Assign(TJclLocationInfoEx(FItems[I]));
  3550. end
  3551. else
  3552. inherited AssignTo(Dest);
  3553. end;
  3554. procedure TJclCustomLocationInfoList.Clear;
  3555. begin
  3556. FItems.Clear;
  3557. end;
  3558. function TJclCustomLocationInfoList.GetAsString: string;
  3559. var
  3560. I: Integer;
  3561. Strings: TStringList;
  3562. begin
  3563. Strings := TStringList.Create;
  3564. try
  3565. for I := 0 to Count - 1 do
  3566. Strings.Add(TJclLocationInfoEx(FItems[I]).AsString);
  3567. Result := Strings.Text;
  3568. finally
  3569. Strings.Free;
  3570. end;
  3571. end;
  3572. function TJclCustomLocationInfoList.GetCount: Integer;
  3573. begin
  3574. Result := FItems.Count;
  3575. end;
  3576. function TJclCustomLocationInfoList.InternalAdd(Addr: Pointer): TJclLocationInfoEx;
  3577. begin
  3578. FItems.Add(FItemClass.Create(Self, Addr));
  3579. Result := TJclLocationInfoEx(FItems.Last);
  3580. end;
  3581. //=== { TJclLocationInfoList } ===============================================
  3582. function TJclLocationInfoList.Add(Addr: Pointer): TJclLocationInfoEx;
  3583. begin
  3584. Result := InternalAdd(Addr);
  3585. end;
  3586. constructor TJclLocationInfoList.Create;
  3587. begin
  3588. inherited Create;
  3589. FOptions := [liloAutoGetAddressInfo, liloAutoGetLocationInfo, liloAutoGetUnitVersionInfo];
  3590. end;
  3591. function TJclLocationInfoList.GetItems(AIndex: Integer): TJclLocationInfoEx;
  3592. begin
  3593. Result := TJclLocationInfoEx(FItems[AIndex]);
  3594. end;
  3595. //=== { TJclDebugInfoSource } ================================================
  3596. constructor TJclDebugInfoSource.Create(AModule: HMODULE);
  3597. var
  3598. MemInfo: TMemoryBasicInformation;
  3599. begin
  3600. FModule := AModule;
  3601. FModuleCodeSize := 0;
  3602. if VirtualQuery(Pointer(TJclAddr(FModule) + ModuleCodeOffset), MemInfo, SizeOf(MemInfo)) = SizeOf(MemInfo) then
  3603. FModuleCodeSize := MemInfo.RegionSize;
  3604. end;
  3605. function TJclDebugInfoSource.GetFileName: TFileName;
  3606. begin
  3607. Result := GetModulePath(FModule);
  3608. end;
  3609. function TJclDebugInfoSource.VAFromAddr(const Addr: Pointer): DWORD;
  3610. begin
  3611. Result := DWORD(TJclAddr(Addr) - TJclAddr(FModule) - ModuleCodeOffset);
  3612. end;
  3613. function TJclDebugInfoSource.AddrFromVA(const VA: DWORD): Pointer;
  3614. begin
  3615. Result := Pointer(TJclAddr(VA) + TJclAddr(FModule) + ModuleCodeOffset);
  3616. end;
  3617. //=== { TJclDebugInfoList } ==================================================
  3618. var
  3619. DebugInfoList: TJclDebugInfoList = nil;
  3620. InfoSourceClassList: TList = nil;
  3621. DebugInfoCritSect: TJclCriticalSection;
  3622. procedure NeedDebugInfoList;
  3623. begin
  3624. if DebugInfoList = nil then
  3625. DebugInfoList := TJclDebugInfoList.Create;
  3626. end;
  3627. function TJclDebugInfoList.CreateDebugInfo(const Module: HMODULE): TJclDebugInfoSource;
  3628. var
  3629. I: Integer;
  3630. begin
  3631. NeedInfoSourceClassList;
  3632. Result := nil;
  3633. for I := 0 to InfoSourceClassList.Count - 1 do
  3634. begin
  3635. Result := TJclDebugInfoSourceClass(InfoSourceClassList.Items[I]).Create(Module);
  3636. try
  3637. if Result.InitializeSource then
  3638. Break
  3639. else
  3640. FreeAndNil(Result);
  3641. except
  3642. Result.Free;
  3643. raise;
  3644. end;
  3645. end;
  3646. end;
  3647. function TJclDebugInfoList.GetItemFromModule(const Module: HMODULE): TJclDebugInfoSource;
  3648. var
  3649. I: Integer;
  3650. TempItem: TJclDebugInfoSource;
  3651. begin
  3652. Result := nil;
  3653. if Module = 0 then
  3654. Exit;
  3655. for I := 0 to Count - 1 do
  3656. begin
  3657. TempItem := Items[I];
  3658. if TempItem.Module = Module then
  3659. begin
  3660. Result := TempItem;
  3661. Break;
  3662. end;
  3663. end;
  3664. if Result = nil then
  3665. begin
  3666. Result := CreateDebugInfo(Module);
  3667. if Result <> nil then
  3668. Add(Result);
  3669. end;
  3670. end;
  3671. function TJclDebugInfoList.GetItems(Index: Integer): TJclDebugInfoSource;
  3672. begin
  3673. Result := TJclDebugInfoSource(Get(Index));
  3674. end;
  3675. function TJclDebugInfoList.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;
  3676. var
  3677. Item: TJclDebugInfoSource;
  3678. begin
  3679. ResetMemory(Info, SizeOf(Info));
  3680. Item := ItemFromModule[CachedModuleFromAddr(Addr)];
  3681. if Item <> nil then
  3682. Result := Item.GetLocationInfo(Addr, Info)
  3683. else
  3684. Result := False;
  3685. end;
  3686. class procedure TJclDebugInfoList.NeedInfoSourceClassList;
  3687. begin
  3688. if not Assigned(InfoSourceClassList) then
  3689. begin
  3690. InfoSourceClassList := TList.Create;
  3691. {$IFNDEF DEBUG_NO_BINARY}
  3692. InfoSourceClassList.Add(Pointer(TJclDebugInfoBinary));
  3693. {$ENDIF !DEBUG_NO_BINARY}
  3694. {$IFNDEF DEBUG_NO_TD32}
  3695. InfoSourceClassList.Add(Pointer(TJclDebugInfoTD32));
  3696. {$ENDIF !DEBUG_NO_TD32}
  3697. {$IFNDEF DEBUG_NO_MAP}
  3698. InfoSourceClassList.Add(Pointer(TJclDebugInfoMap));
  3699. {$ENDIF !DEBUG_NO_MAP}
  3700. {$IFNDEF DEBUG_NO_SYMBOLS}
  3701. InfoSourceClassList.Add(Pointer(TJclDebugInfoSymbols));
  3702. {$ENDIF !DEBUG_NO_SYMBOLS}
  3703. {$IFNDEF DEBUG_NO_EXPORTS}
  3704. InfoSourceClassList.Add(Pointer(TJclDebugInfoExports));
  3705. {$ENDIF !DEBUG_NO_EXPORTS}
  3706. end;
  3707. end;
  3708. class procedure TJclDebugInfoList.RegisterDebugInfoSource(
  3709. const InfoSourceClass: TJclDebugInfoSourceClass);
  3710. begin
  3711. NeedInfoSourceClassList;
  3712. InfoSourceClassList.Add(Pointer(InfoSourceClass));
  3713. end;
  3714. class procedure TJclDebugInfoList.RegisterDebugInfoSourceFirst(
  3715. const InfoSourceClass: TJclDebugInfoSourceClass);
  3716. begin
  3717. NeedInfoSourceClassList;
  3718. InfoSourceClassList.Insert(0, Pointer(InfoSourceClass));
  3719. end;
  3720. class procedure TJclDebugInfoList.UnRegisterDebugInfoSource(
  3721. const InfoSourceClass: TJclDebugInfoSourceClass);
  3722. begin
  3723. if Assigned(InfoSourceClassList) then
  3724. InfoSourceClassList.Remove(Pointer(InfoSourceClass));
  3725. end;
  3726. //=== { TJclDebugInfoMap } ===================================================
  3727. destructor TJclDebugInfoMap.Destroy;
  3728. begin
  3729. FreeAndNil(FScanner);
  3730. inherited Destroy;
  3731. end;
  3732. function TJclDebugInfoMap.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;
  3733. var
  3734. VA: DWORD;
  3735. begin
  3736. VA := VAFromAddr(Addr);
  3737. with FScanner do
  3738. begin
  3739. Info.UnitName := ModuleNameFromAddr(VA);
  3740. Result := Info.UnitName <> '';
  3741. if Result then
  3742. begin
  3743. Info.Address := Addr;
  3744. Info.ProcedureName := ProcNameFromAddr(VA, Info.OffsetFromProcName);
  3745. Info.LineNumber := LineNumberFromAddr(VA, Info.OffsetFromLineNumber);
  3746. Info.SourceName := SourceNameFromAddr(VA);
  3747. Info.DebugInfo := Self;
  3748. Info.BinaryFileName := FileName;
  3749. end;
  3750. end;
  3751. end;
  3752. function TJclDebugInfoMap.GetAddress(const UnitName, ProcName: string): Pointer;
  3753. var
  3754. VA: DWORD;
  3755. begin
  3756. Result := nil;
  3757. VA := FScanner.VAFromUnitAndProcName(UnitName, ProcName);
  3758. if VA <> 0 then
  3759. Result := AddrFromVA(VA);
  3760. end;
  3761. function TJclDebugInfoMap.InitializeSource: Boolean;
  3762. var
  3763. MapFileName: TFileName;
  3764. begin
  3765. MapFileName := ChangeFileExt(FileName, JclMapFileExtension);
  3766. Result := FileExists(MapFileName);
  3767. if Result then
  3768. FScanner := TJclMapScanner.Create(MapFileName, Module);
  3769. end;
  3770. //=== { TJclDebugInfoBinary } ================================================
  3771. destructor TJclDebugInfoBinary.Destroy;
  3772. begin
  3773. FreeAndNil(FScanner);
  3774. FreeAndNil(FStream);
  3775. inherited Destroy;
  3776. end;
  3777. function TJclDebugInfoBinary.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;
  3778. var
  3779. VA: DWORD;
  3780. begin
  3781. VA := VAFromAddr(Addr);
  3782. with FScanner do
  3783. begin
  3784. Info.UnitName := ModuleNameFromAddr(VA);
  3785. Result := Info.UnitName <> '';
  3786. if Result then
  3787. begin
  3788. Info.Address := Addr;
  3789. Info.ProcedureName := ProcNameFromAddr(VA, Info.OffsetFromProcName);
  3790. Info.LineNumber := LineNumberFromAddr(VA, Info.OffsetFromLineNumber);
  3791. Info.SourceName := SourceNameFromAddr(VA);
  3792. Info.DebugInfo := Self;
  3793. Info.BinaryFileName := FileName;
  3794. end;
  3795. end;
  3796. end;
  3797. function TJclDebugInfoBinary.GetAddress(const UnitName, ProcName: string): Pointer;
  3798. var
  3799. VA: DWORD;
  3800. begin
  3801. Result := nil;
  3802. VA := FScanner.VAFromUnitAndProcName(UnitName, ProcName);
  3803. if VA <> 0 then
  3804. Result := AddrFromVA(VA);
  3805. end;
  3806. function TJclDebugInfoBinary.InitializeSource: Boolean;
  3807. var
  3808. JdbgFileName: TFileName;
  3809. VerifyFileName: Boolean;
  3810. begin
  3811. VerifyFileName := False;
  3812. Result := (PeMapImgFindSectionFromModule(Pointer(Module), JclDbgDataResName) <> nil);
  3813. if Result then
  3814. FStream := TJclPeSectionStream.Create(Module, JclDbgDataResName)
  3815. else
  3816. begin
  3817. JdbgFileName := ChangeFileExt(FileName, JclDbgFileExtension);
  3818. Result := FileExists(JdbgFileName);
  3819. if Result then
  3820. begin
  3821. FStream := TJclFileMappingStream.Create(JdbgFileName, fmOpenRead or fmShareDenyWrite);
  3822. VerifyFileName := True;
  3823. end;
  3824. end;
  3825. if Result then
  3826. begin
  3827. FScanner := TJclBinDebugScanner.Create(FStream, True, False);
  3828. Result := FScanner.ValidFormat and
  3829. (not VerifyFileName or FScanner.IsModuleNameValid(FileName));
  3830. end;
  3831. end;
  3832. //=== { TJclDebugInfoExports } ===============================================
  3833. destructor TJclDebugInfoExports.Destroy;
  3834. begin
  3835. FreeAndNil(FImage);
  3836. inherited Destroy;
  3837. end;
  3838. function TJclDebugInfoExports.IsAddressInThisExportedFunction(Addr: PByteArray; FunctionStartAddr: TJclAddr): Boolean;
  3839. begin
  3840. Dec(TJclAddr(Addr), 6);
  3841. Result := False;
  3842. while TJclAddr(Addr) > FunctionStartAddr do
  3843. begin
  3844. if IsBadReadPtr(Addr, 6) then
  3845. Exit;
  3846. if (Addr[0] = $C2) and // ret $xxxx
  3847. (((Addr[3] = $90) and (Addr[4] = $90) and (Addr[5] = $90)) or // nop
  3848. ((Addr[3] = $CC) and (Addr[4] = $CC) and (Addr[5] = $CC))) then // int 3
  3849. Exit;
  3850. if (Addr[0] = $C3) and // ret
  3851. (((Addr[1] = $90) and (Addr[2] = $90) and (Addr[3] = $90)) or // nop
  3852. ((Addr[1] = $CC) and (Addr[2] = $CC) and (Addr[3] = $CC))) then // int 3
  3853. Exit;
  3854. if (Addr[0] = $E9) and // jmp rel-far
  3855. (((Addr[5] = $90) and (Addr[6] = $90) and (Addr[7] = $90)) or // nop
  3856. ((Addr[5] = $CC) and (Addr[6] = $CC) and (Addr[7] = $CC))) then // int 3
  3857. Exit;
  3858. if (Addr[0] = $EB) and // jmp rel-near
  3859. (((Addr[2] = $90) and (Addr[3] = $90) and (Addr[4] = $90)) or // nop
  3860. ((Addr[2] = $CC) and (Addr[3] = $CC) and (Addr[4] = $CC))) then // int 3
  3861. Exit;
  3862. Dec(TJclAddr(Addr));
  3863. end;
  3864. Result := True;
  3865. end;
  3866. function TJclDebugInfoExports.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;
  3867. var
  3868. I, BasePos: Integer;
  3869. VA: DWORD;
  3870. Desc: TJclBorUmDescription;
  3871. Unmangled: string;
  3872. RawName: Boolean;
  3873. begin
  3874. Result := False;
  3875. VA := DWORD(TJclAddr(Addr) - TJclAddr(FModule));
  3876. {$IFDEF BORLAND}
  3877. RawName := not FImage.IsPackage;
  3878. {$ENDIF BORLAND}
  3879. {$IFDEF FPC}
  3880. RawName := True;
  3881. {$ENDIF FPC}
  3882. Info.OffsetFromProcName := 0;
  3883. Info.OffsetFromLineNumber := 0;
  3884. Info.BinaryFileName := FileName;
  3885. with FImage.ExportList do
  3886. begin
  3887. SortList(esAddress, False);
  3888. for I := Count - 1 downto 0 do
  3889. if Items[I].Address <= VA then
  3890. begin
  3891. if RawName then
  3892. begin
  3893. Info.ProcedureName := Items[I].Name;
  3894. Info.OffsetFromProcName := VA - Items[I].Address;
  3895. Result := True;
  3896. end
  3897. else
  3898. begin
  3899. case PeBorUnmangleName(Items[I].Name, Unmangled, Desc, BasePos) of
  3900. urOk:
  3901. begin
  3902. Info.UnitName := Copy(Unmangled, 1, BasePos - 2);
  3903. if not (Desc.Kind in [skRTTI, skVTable]) then
  3904. begin
  3905. Info.ProcedureName := Copy(Unmangled, BasePos, Length(Unmangled));
  3906. if smLinkProc in Desc.Modifiers then
  3907. Info.ProcedureName := '@' + Info.ProcedureName;
  3908. Info.OffsetFromProcName := VA - Items[I].Address;
  3909. end;
  3910. Result := True;
  3911. end;
  3912. urNotMangled:
  3913. begin
  3914. Info.ProcedureName := Items[I].Name;
  3915. Info.OffsetFromProcName := VA - Items[I].Address;
  3916. Result := True;
  3917. end;
  3918. end;
  3919. end;
  3920. if Result then
  3921. begin
  3922. Info.Address := Addr;
  3923. Info.DebugInfo := Self;
  3924. { Check if we have a valid address in an exported function. }
  3925. if not IsAddressInThisExportedFunction(Addr, FModule + Items[I].Address) then
  3926. begin
  3927. //Info.UnitName := '[' + AnsiLowerCase(ExtractFileName(GetModulePath(FModule))) + ']'
  3928. Info.ProcedureName := Format(LoadResString(@RsUnknownFunctionAt), [Info.ProcedureName]);
  3929. end;
  3930. Break;
  3931. end;
  3932. end;
  3933. end;
  3934. end;
  3935. function TJclDebugInfoExports.GetAddress(const UnitName, ProcName: string): Pointer;
  3936. var
  3937. I, BasePos: Integer;
  3938. Desc: TJclBorUmDescription;
  3939. RawName: Boolean;
  3940. ItemUnitName: string;
  3941. Unmangled: string;
  3942. begin
  3943. Result := nil;
  3944. {$IFDEF BORLAND}
  3945. RawName := not FImage.IsPackage;
  3946. {$ENDIF BORLAND}
  3947. {$IFDEF FPC}
  3948. RawName := True;
  3949. {$ENDIF FPC}
  3950. with FImage.ExportList do
  3951. begin
  3952. // SortList(esAddress, False);
  3953. for I := 0 to Count - 1 do
  3954. begin
  3955. if RawName then
  3956. begin
  3957. ItemUnitName := '';
  3958. Unmangled := Items[I].Name;
  3959. end
  3960. else
  3961. begin
  3962. case PeBorUnmangleName(Items[I].Name, Unmangled, Desc, BasePos) of
  3963. urOk:
  3964. begin
  3965. ItemUnitName := Copy(Unmangled, 1, BasePos - 2);
  3966. if not (Desc.Kind in [skRTTI, skVTable]) then
  3967. begin
  3968. Unmangled := Copy(Unmangled, BasePos, Length(Unmangled));
  3969. if smLinkProc in Desc.Modifiers then
  3970. Unmangled := '@' + Unmangled;
  3971. end;
  3972. end;
  3973. urNotMangled:
  3974. Unmangled := Items[I].Name;
  3975. end;
  3976. end;
  3977. if ((ItemUnitName = '') or (CompareStr(ItemUnitName, UnitName) = 0)) and (CompareStr(Unmangled, ProcName) = 0) then
  3978. begin
  3979. Result := AddrFromVA(Items[I].Address);
  3980. Break;
  3981. end;
  3982. end;
  3983. end;
  3984. end;
  3985. function TJclDebugInfoExports.InitializeSource: Boolean;
  3986. begin
  3987. {$IFDEF BORLAND}
  3988. FImage := TJclPeBorImage.Create(True);
  3989. {$ENDIF BORLAND}
  3990. {$IFDEF FPC}
  3991. FImage := TJclPeImage.Create(True);
  3992. {$ENDIF FPC}
  3993. FImage.AttachLoadedModule(FModule);
  3994. Result := FImage.StatusOK and (FImage.ExportList.Count > 0);
  3995. end;
  3996. {$IFDEF BORLAND}
  3997. //=== { TJclDebugInfoTD32 } ==================================================
  3998. destructor TJclDebugInfoTD32.Destroy;
  3999. begin
  4000. FreeAndNil(FImage);
  4001. inherited Destroy;
  4002. end;
  4003. function TJclDebugInfoTD32.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;
  4004. var
  4005. VA: DWORD;
  4006. begin
  4007. VA := VAFromAddr(Addr);
  4008. Info.UnitName := FImage.TD32Scanner.ModuleNameFromAddr(VA);
  4009. Result := Info.UnitName <> '';
  4010. if Result then
  4011. with Info do
  4012. begin
  4013. Address := Addr;
  4014. ProcedureName := FImage.TD32Scanner.ProcNameFromAddr(VA, OffsetFromProcName);
  4015. LineNumber := FImage.TD32Scanner.LineNumberFromAddr(VA, OffsetFromLineNumber);
  4016. SourceName := FImage.TD32Scanner.SourceNameFromAddr(VA);
  4017. DebugInfo := Self;
  4018. BinaryFileName := FileName;
  4019. end;
  4020. end;
  4021. function TJclDebugInfoTD32.GetAddress(const UnitName, ProcName: string): Pointer;
  4022. var
  4023. VA: DWORD;
  4024. begin
  4025. Result := nil;
  4026. VA := FImage.TD32Scanner.VAFromUnitAndProcName(UnitName, ProcName);
  4027. if VA <> 0 then
  4028. Result := AddrFromVA(VA);
  4029. end;
  4030. function TJclDebugInfoTD32.InitializeSource: Boolean;
  4031. begin
  4032. FImage := TJclPeBorTD32Image.Create(True);
  4033. try
  4034. FImage.AttachLoadedModule(Module);
  4035. Result := FImage.IsTD32DebugPresent;
  4036. except
  4037. Result := False;
  4038. end;
  4039. end;
  4040. procedure TJclDebugInfoTD32.GenerateUnmangledNames;
  4041. begin
  4042. FImage.TD32Scanner.GenerateUnmangledNames;
  4043. end;
  4044. {$ENDIF BORLAND}
  4045. //=== { TJclDebugInfoSymbols } ===============================================
  4046. type
  4047. TSymInitializeAFunc = function (hProcess: THandle; UserSearchPath: LPSTR;
  4048. fInvadeProcess: Bool): Bool; stdcall;
  4049. TSymInitializeWFunc = function (hProcess: THandle; UserSearchPath: LPWSTR;
  4050. fInvadeProcess: Bool): Bool; stdcall;
  4051. TSymGetOptionsFunc = function: DWORD; stdcall;
  4052. TSymSetOptionsFunc = function (SymOptions: DWORD): DWORD; stdcall;
  4053. TSymCleanupFunc = function (hProcess: THandle): Bool; stdcall;
  4054. {$IFDEF CPU32}
  4055. TSymGetSymFromAddrAFunc = function (hProcess: THandle; dwAddr: DWORD;
  4056. pdwDisplacement: PDWORD; var Symbol: JclWin32.TImagehlpSymbolA): Bool; stdcall;
  4057. TSymGetSymFromAddrWFunc = function (hProcess: THandle; dwAddr: DWORD;
  4058. pdwDisplacement: PDWORD; var Symbol: JclWin32.TImagehlpSymbolW): Bool; stdcall;
  4059. TSymGetModuleInfoAFunc = function (hProcess: THandle; dwAddr: DWORD;
  4060. var ModuleInfo: JclWin32.TImagehlpModuleA): Bool; stdcall;
  4061. TSymGetModuleInfoWFunc = function (hProcess: THandle; dwAddr: DWORD;
  4062. var ModuleInfo: JclWin32.TImagehlpModuleW): Bool; stdcall;
  4063. TSymLoadModuleFunc = function (hProcess: THandle; hFile: THandle; ImageName,
  4064. ModuleName: LPSTR; BaseOfDll: DWORD; SizeOfDll: DWORD): DWORD; stdcall;
  4065. TSymGetLineFromAddrAFunc = function (hProcess: THandle; dwAddr: DWORD;
  4066. pdwDisplacement: PDWORD; var Line: JclWin32.TImageHlpLineA): Bool; stdcall;
  4067. TSymGetLineFromAddrWFunc = function (hProcess: THandle; dwAddr: DWORD;
  4068. pdwDisplacement: PDWORD; var Line: JclWin32.TImageHlpLineW): Bool; stdcall;
  4069. {$ENDIF CPU32}
  4070. {$IFDEF CPU64}
  4071. TSymGetSymFromAddrAFunc = function (hProcess: THandle; dwAddr: DWORD64;
  4072. pdwDisplacement: PDWORD64; var Symbol: JclWin32.TImagehlpSymbolA64): Bool; stdcall;
  4073. TSymGetSymFromAddrWFunc = function (hProcess: THandle; dwAddr: DWORD64;
  4074. pdwDisplacement: PDWORD64; var Symbol: JclWin32.TImagehlpSymbolW64): Bool; stdcall;
  4075. TSymGetModuleInfoAFunc = function (hProcess: THandle; dwAddr: DWORD64;
  4076. var ModuleInfo: JclWin32.TImagehlpModuleA64): Bool; stdcall;
  4077. TSymGetModuleInfoWFunc = function (hProcess: THandle; dwAddr: DWORD64;
  4078. var ModuleInfo: JclWin32.TImagehlpModuleW64): Bool; stdcall;
  4079. TSymLoadModuleFunc = function (hProcess: THandle; hFile: THandle; ImageName,
  4080. ModuleName: LPSTR; BaseOfDll: DWORD64; SizeOfDll: DWORD): DWORD; stdcall;
  4081. TSymGetLineFromAddrAFunc = function (hProcess: THandle; dwAddr: DWORD64;
  4082. pdwDisplacement: PDWORD; var Line: JclWin32.TImageHlpLineA64): Bool; stdcall;
  4083. TSymGetLineFromAddrWFunc = function (hProcess: THandle; dwAddr: DWORD64;
  4084. pdwDisplacement: PDWORD; var Line: JclWin32.TImageHlpLineW64): Bool; stdcall;
  4085. {$ENDIF CPU64}
  4086. var
  4087. DebugSymbolsInitialized: Boolean = False;
  4088. DebugSymbolsLoadFailed: Boolean = False;
  4089. ImageHlpDllHandle: THandle = 0;
  4090. SymInitializeAFunc: TSymInitializeAFunc = nil;
  4091. SymInitializeWFunc: TSymInitializeWFunc = nil;
  4092. SymGetOptionsFunc: TSymGetOptionsFunc = nil;
  4093. SymSetOptionsFunc: TSymSetOptionsFunc = nil;
  4094. SymCleanupFunc: TSymCleanupFunc = nil;
  4095. SymGetSymFromAddrAFunc: TSymGetSymFromAddrAFunc = nil;
  4096. SymGetSymFromAddrWFunc: TSymGetSymFromAddrWFunc = nil;
  4097. SymGetModuleInfoAFunc: TSymGetModuleInfoAFunc = nil;
  4098. SymGetModuleInfoWFunc: TSymGetModuleInfoWFunc = nil;
  4099. SymLoadModuleFunc: TSymLoadModuleFunc = nil;
  4100. SymGetLineFromAddrAFunc: TSymGetLineFromAddrAFunc = nil;
  4101. SymGetLineFromAddrWFunc: TSymGetLineFromAddrWFunc = nil;
  4102. const
  4103. ImageHlpDllName = 'imagehlp.dll'; // do not localize
  4104. SymInitializeAFuncName = 'SymInitialize'; // do not localize
  4105. SymInitializeWFuncName = 'SymInitializeW'; // do not localize
  4106. SymGetOptionsFuncName = 'SymGetOptions'; // do not localize
  4107. SymSetOptionsFuncName = 'SymSetOptions'; // do not localize
  4108. SymCleanupFuncName = 'SymCleanup'; // do not localize
  4109. {$IFDEF CPU32}
  4110. SymGetSymFromAddrAFuncName = 'SymGetSymFromAddr'; // do not localize
  4111. SymGetSymFromAddrWFuncName = 'SymGetSymFromAddrW'; // do not localize
  4112. SymGetModuleInfoAFuncName = 'SymGetModuleInfo'; // do not localize
  4113. SymGetModuleInfoWFuncName = 'SymGetModuleInfoW'; // do not localize
  4114. SymLoadModuleFuncName = 'SymLoadModule'; // do not localize
  4115. SymGetLineFromAddrAFuncName = 'SymGetLineFromAddr'; // do not localize
  4116. SymGetLineFromAddrWFuncName = 'SymGetLineFromAddrW'; // do not localize
  4117. {$ENDIF CPU32}
  4118. {$IFDEF CPU64}
  4119. SymGetSymFromAddrAFuncName = 'SymGetSymFromAddr64'; // do not localize
  4120. SymGetSymFromAddrWFuncName = 'SymGetSymFromAddrW64'; // do not localize
  4121. SymGetModuleInfoAFuncName = 'SymGetModuleInfo64'; // do not localize
  4122. SymGetModuleInfoWFuncName = 'SymGetModuleInfoW64'; // do not localize
  4123. SymLoadModuleFuncName = 'SymLoadModule64'; // do not localize
  4124. SymGetLineFromAddrAFuncName = 'SymGetLineFromAddr64'; // do not localize
  4125. SymGetLineFromAddrWFuncName = 'SymGetLineFromAddrW64'; // do not localize
  4126. {$ENDIF CPU64}
  4127. function StrRemoveEmptyPaths(const Paths: string): string;
  4128. var
  4129. List: TStrings;
  4130. I: Integer;
  4131. begin
  4132. List := TStringList.Create;
  4133. try
  4134. StrToStrings(Paths, DirSeparator, List, False);
  4135. for I := 0 to List.Count - 1 do
  4136. if Trim(List[I]) = '' then
  4137. List[I] := '';
  4138. Result := StringsToStr(List, DirSeparator, False);
  4139. finally
  4140. List.Free;
  4141. end;
  4142. end;
  4143. class function TJclDebugInfoSymbols.InitializeDebugSymbols: Boolean;
  4144. var
  4145. EnvironmentVarValue, SearchPath: string;
  4146. SymOptions: Cardinal;
  4147. ProcessHandle: THandle;
  4148. begin
  4149. Result := DebugSymbolsInitialized;
  4150. if not DebugSymbolsLoadFailed then
  4151. begin
  4152. Result := LoadDebugFunctions;
  4153. DebugSymbolsLoadFailed := not Result;
  4154. if Result then
  4155. begin
  4156. if JclDebugInfoSymbolPaths <> '' then
  4157. begin
  4158. SearchPath := StrEnsureSuffix(DirSeparator, JclDebugInfoSymbolPaths);
  4159. SearchPath := StrEnsureNoSuffix(DirSeparator, SearchPath + GetCurrentFolder);
  4160. if GetEnvironmentVar(EnvironmentVarNtSymbolPath, EnvironmentVarValue) and (EnvironmentVarValue <> '') then
  4161. SearchPath := StrEnsureNoSuffix(DirSeparator, StrEnsureSuffix(DirSeparator, EnvironmentVarValue) + SearchPath);
  4162. if GetEnvironmentVar(EnvironmentVarAlternateNtSymbolPath, EnvironmentVarValue) and (EnvironmentVarValue <> '') then
  4163. SearchPath := StrEnsureNoSuffix(DirSeparator, StrEnsureSuffix(DirSeparator, EnvironmentVarValue) + SearchPath);
  4164. // DbgHelp.dll crashes when an empty path is specified.
  4165. // This also means that the SearchPath must not end with a DirSeparator. }
  4166. SearchPath := StrRemoveEmptyPaths(SearchPath);
  4167. end
  4168. else
  4169. // Fix crash SymLoadModuleFunc on WinXP SP3 when SearchPath=''
  4170. SearchPath := GetCurrentFolder;
  4171. if IsWinNT then
  4172. // in Windows NT, first argument is a process handle
  4173. ProcessHandle := GetCurrentProcess
  4174. else
  4175. // in Windows 95, 98, ME first argument is a process identifier
  4176. ProcessHandle := GetCurrentProcessId;
  4177. // Debug(WinXPSP3): SymInitializeWFunc==nil
  4178. if Assigned(SymInitializeWFunc) then
  4179. Result := SymInitializeWFunc(ProcessHandle, PWideChar(WideString(SearchPath)), False)
  4180. else
  4181. if Assigned(SymInitializeAFunc) then
  4182. Result := SymInitializeAFunc(ProcessHandle, PAnsiChar(AnsiString(SearchPath)), False)
  4183. else
  4184. Result := False;
  4185. if Result then
  4186. begin
  4187. SymOptions := SymGetOptionsFunc or SYMOPT_DEFERRED_LOADS
  4188. or SYMOPT_FAIL_CRITICAL_ERRORS or SYMOPT_INCLUDE_32BIT_MODULES or SYMOPT_LOAD_LINES;
  4189. SymOptions := SymOptions and (not (SYMOPT_NO_UNQUALIFIED_LOADS or SYMOPT_UNDNAME));
  4190. SymSetOptionsFunc(SymOptions);
  4191. end;
  4192. DebugSymbolsInitialized := Result;
  4193. end
  4194. else
  4195. UnloadDebugFunctions;
  4196. end;
  4197. end;
  4198. class function TJclDebugInfoSymbols.CleanupDebugSymbols: Boolean;
  4199. begin
  4200. Result := True;
  4201. if DebugSymbolsInitialized then
  4202. Result := SymCleanupFunc(GetCurrentProcess);
  4203. UnloadDebugFunctions;
  4204. end;
  4205. function TJclDebugInfoSymbols.GetLocationInfo(const Addr: Pointer;
  4206. out Info: TJclLocationInfo): Boolean;
  4207. const
  4208. SymbolNameLength = 1000;
  4209. {$IFDEF CPU32}
  4210. SymbolSizeA = SizeOf(TImagehlpSymbolA) + SymbolNameLength * SizeOf(AnsiChar);
  4211. SymbolSizeW = SizeOf(TImagehlpSymbolW) + SymbolNameLength * SizeOf(WideChar);
  4212. {$ENDIF CPU32}
  4213. {$IFDEF CPU64}
  4214. SymbolSizeA = SizeOf(TImagehlpSymbolA64) + SymbolNameLength * SizeOf(AnsiChar);
  4215. SymbolSizeW = SizeOf(TImagehlpSymbolW64) + SymbolNameLength * SizeOf(WideChar);
  4216. {$ENDIF CPU64}
  4217. var
  4218. Displacement: DWORD;
  4219. ProcessHandle: THandle;
  4220. {$IFDEF CPU32}
  4221. SymbolA: PImagehlpSymbolA;
  4222. SymbolW: PImagehlpSymbolW;
  4223. LineA: TImageHlpLineA;
  4224. LineW: TImageHlpLineW;
  4225. {$ENDIF CPU32}
  4226. {$IFDEF CPU64}
  4227. SymbolA: PImagehlpSymbolA64;
  4228. SymbolW: PImagehlpSymbolW64;
  4229. LineA: TImageHlpLineA64;
  4230. LineW: TImageHlpLineW64;
  4231. {$ENDIF CPU64}
  4232. begin
  4233. ProcessHandle := GetCurrentProcess;
  4234. if Assigned(SymGetSymFromAddrWFunc) then
  4235. begin
  4236. GetMem(SymbolW, SymbolSizeW);
  4237. try
  4238. ZeroMemory(SymbolW, SymbolSizeW);
  4239. SymbolW^.SizeOfStruct := SizeOf(SymbolW^);
  4240. SymbolW^.MaxNameLength := SymbolNameLength;
  4241. Displacement := 0;
  4242. Result := SymGetSymFromAddrWFunc(ProcessHandle, TJclAddr(Addr), @Displacement, SymbolW^);
  4243. if Result then
  4244. begin
  4245. Info.DebugInfo := Self;
  4246. Info.Address := Addr;
  4247. Info.BinaryFileName := FileName;
  4248. Info.OffsetFromProcName := Displacement;
  4249. JclPeImage.UnDecorateSymbolName(string(PWideChar(@SymbolW^.Name[0])), Info.ProcedureName, UNDNAME_NAME_ONLY or UNDNAME_NO_ARGUMENTS);
  4250. end;
  4251. finally
  4252. FreeMem(SymbolW);
  4253. end;
  4254. end
  4255. else
  4256. if Assigned(SymGetSymFromAddrAFunc) then
  4257. begin
  4258. GetMem(SymbolA, SymbolSizeA);
  4259. try
  4260. ZeroMemory(SymbolA, SymbolSizeA);
  4261. SymbolA^.SizeOfStruct := SizeOf(SymbolA^);
  4262. SymbolA^.MaxNameLength := SymbolNameLength;
  4263. Displacement := 0;
  4264. Result := SymGetSymFromAddrAFunc(ProcessHandle, TJclAddr(Addr), @Displacement, SymbolA^);
  4265. if Result then
  4266. begin
  4267. Info.DebugInfo := Self;
  4268. Info.Address := Addr;
  4269. Info.BinaryFileName := FileName;
  4270. Info.OffsetFromProcName := Displacement;
  4271. JclPeImage.UnDecorateSymbolName(string(PAnsiChar(@SymbolA^.Name[0])), Info.ProcedureName, UNDNAME_NAME_ONLY or UNDNAME_NO_ARGUMENTS);
  4272. end;
  4273. finally
  4274. FreeMem(SymbolA);
  4275. end;
  4276. end
  4277. else
  4278. Result := False;
  4279. // line number is optional
  4280. if Result and Assigned(SymGetLineFromAddrWFunc) then
  4281. begin
  4282. ZeroMemory(@LineW, SizeOf(LineW));
  4283. LineW.SizeOfStruct := SizeOf(LineW);
  4284. Displacement := 0;
  4285. if SymGetLineFromAddrWFunc(ProcessHandle, TJclAddr(Addr), @Displacement, LineW) then
  4286. begin
  4287. Info.LineNumber := LineW.LineNumber;
  4288. Info.UnitName := string(LineW.FileName);
  4289. Info.OffsetFromLineNumber := Displacement;
  4290. end;
  4291. end
  4292. else
  4293. if Result and Assigned(SymGetLineFromAddrAFunc) then
  4294. begin
  4295. ZeroMemory(@LineA, SizeOf(LineA));
  4296. LineA.SizeOfStruct := SizeOf(LineA);
  4297. Displacement := 0;
  4298. if SymGetLineFromAddrAFunc(ProcessHandle, TJclAddr(Addr), @Displacement, LineA) then
  4299. begin
  4300. Info.LineNumber := LineA.LineNumber;
  4301. Info.UnitName := string(LineA.FileName);
  4302. Info.OffsetFromLineNumber := Displacement;
  4303. end;
  4304. end;
  4305. end;
  4306. function TJclDebugInfoSymbols.GetAddress(const UnitName, ProcName: string): Pointer;
  4307. var
  4308. VA: DWORD;
  4309. begin
  4310. Result := nil;
  4311. VA := 0; // FScanner.VAFromUnitAndProcName(UnitName, ProcName);
  4312. if VA <> 0 then
  4313. Result := AddrFromVA(VA);
  4314. end;
  4315. function TJclDebugInfoSymbols.InitializeSource: Boolean;
  4316. var
  4317. ModuleFileName: TFileName;
  4318. {$IFDEF CPU32}
  4319. ModuleInfoA: TImagehlpModuleA;
  4320. ModuleInfoW: TImagehlpModuleW;
  4321. {$ENDIF CPU32}
  4322. {$IFDEF CPU64}
  4323. ModuleInfoA: TImagehlpModuleA64;
  4324. ModuleInfoW: TImagehlpModuleW64;
  4325. {$ENDIF CPU64}
  4326. ProcessHandle: THandle;
  4327. begin
  4328. Result := InitializeDebugSymbols;
  4329. if Result then
  4330. begin
  4331. if IsWinNT then
  4332. // in Windows NT, first argument is a process handle
  4333. ProcessHandle := GetCurrentProcess
  4334. else
  4335. // in Windows 95, 98, ME, first argument is a process identifier
  4336. ProcessHandle := GetCurrentProcessId;
  4337. if Assigned(SymGetModuleInfoWFunc) then
  4338. begin
  4339. ZeroMemory(@ModuleInfoW, SizeOf(ModuleInfoW));
  4340. ModuleInfoW.SizeOfStruct := SizeOf(ModuleInfoW);
  4341. Result := SymGetModuleInfoWFunc(ProcessHandle, Module, ModuleInfoW);
  4342. if not Result then
  4343. begin
  4344. // the symbols for this module are not loaded yet: load the module and query for the symbol again
  4345. ModuleFileName := GetModulePath(Module);
  4346. ZeroMemory(@ModuleInfoW, SizeOf(ModuleInfoW));
  4347. ModuleInfoW.SizeOfStruct := SizeOf(ModuleInfoW);
  4348. // warning: crash on WinXP SP3 when SymInitializeAFunc is called with empty SearchPath
  4349. // OF: possible loss of data
  4350. Result := (SymLoadModuleFunc(ProcessHandle, 0, PAnsiChar(AnsiString(ModuleFileName)), nil, 0, 0) <> 0) and
  4351. SymGetModuleInfoWFunc(ProcessHandle, Module, ModuleInfoW);
  4352. end;
  4353. Result := Result and (ModuleInfoW.BaseOfImage <> 0) and
  4354. not (ModuleInfoW.SymType in [SymNone, SymExport]);
  4355. end
  4356. else
  4357. if Assigned(SymGetModuleInfoAFunc) then
  4358. begin
  4359. ZeroMemory(@ModuleInfoA, SizeOf(ModuleInfoA));
  4360. ModuleInfoA.SizeOfStruct := SizeOf(ModuleInfoA);
  4361. Result := SymGetModuleInfoAFunc(ProcessHandle, Module, ModuleInfoA);
  4362. if not Result then
  4363. begin
  4364. // the symbols for this module are not loaded yet: load the module and query for the symbol again
  4365. ModuleFileName := GetModulePath(Module);
  4366. ZeroMemory(@ModuleInfoA, SizeOf(ModuleInfoA));
  4367. ModuleInfoA.SizeOfStruct := SizeOf(ModuleInfoA);
  4368. // warning: crash on WinXP SP3 when SymInitializeAFunc is called with empty SearchPath
  4369. // OF: possible loss of data
  4370. Result := (SymLoadModuleFunc(ProcessHandle, 0, PAnsiChar(AnsiString(ModuleFileName)), nil, 0, 0) <> 0) and
  4371. SymGetModuleInfoAFunc(ProcessHandle, Module, ModuleInfoA);
  4372. end;
  4373. Result := Result and (ModuleInfoA.BaseOfImage <> 0) and
  4374. not (ModuleInfoA.SymType in [SymNone, SymExport]);
  4375. end
  4376. else
  4377. Result := False;
  4378. end;
  4379. end;
  4380. class function TJclDebugInfoSymbols.LoadDebugFunctions: Boolean;
  4381. begin
  4382. ImageHlpDllHandle := SafeLoadLibrary(ImageHlpDllName);
  4383. if ImageHlpDllHandle <> 0 then
  4384. begin
  4385. SymInitializeAFunc := GetProcAddress(ImageHlpDllHandle, SymInitializeAFuncName);
  4386. SymInitializeWFunc := GetProcAddress(ImageHlpDllHandle, SymInitializeWFuncName);
  4387. SymGetOptionsFunc := GetProcAddress(ImageHlpDllHandle, SymGetOptionsFuncName);
  4388. SymSetOptionsFunc := GetProcAddress(ImageHlpDllHandle, SymSetOptionsFuncName);
  4389. SymCleanupFunc := GetProcAddress(ImageHlpDllHandle, SymCleanupFuncName);
  4390. SymGetSymFromAddrAFunc := GetProcAddress(ImageHlpDllHandle, SymGetSymFromAddrAFuncName);
  4391. SymGetSymFromAddrWFunc := GetProcAddress(ImageHlpDllHandle, SymGetSymFromAddrWFuncName);
  4392. SymGetModuleInfoAFunc := GetProcAddress(ImageHlpDllHandle, SymGetModuleInfoAFuncName);
  4393. SymGetModuleInfoWFunc := GetProcAddress(ImageHlpDllHandle, SymGetModuleInfoWFuncName);
  4394. SymLoadModuleFunc := GetProcAddress(ImageHlpDllHandle, SymLoadModuleFuncName);
  4395. SymGetLineFromAddrAFunc := GetProcAddress(ImageHlpDllHandle, SymGetLineFromAddrAFuncName);
  4396. SymGetLineFromAddrWFunc := GetProcAddress(ImageHlpDllHandle, SymGetLineFromAddrWFuncName);
  4397. end;
  4398. // SymGetLineFromAddrFunc is optional
  4399. Result := (ImageHlpDllHandle <> 0) and
  4400. Assigned(SymGetOptionsFunc) and Assigned(SymSetOptionsFunc) and
  4401. Assigned(SymCleanupFunc) and Assigned(SymLoadModuleFunc) and
  4402. (Assigned(SymInitializeAFunc) or Assigned(SymInitializeWFunc)) and
  4403. (Assigned(SymGetSymFromAddrAFunc) or Assigned(SymGetSymFromAddrWFunc)) and
  4404. (Assigned(SymGetModuleInfoAFunc) or Assigned(SymGetModuleInfoWFunc));
  4405. end;
  4406. class function TJclDebugInfoSymbols.UnloadDebugFunctions: Boolean;
  4407. begin
  4408. Result := ImageHlpDllHandle <> 0;
  4409. if Result then
  4410. FreeLibrary(ImageHlpDllHandle);
  4411. ImageHlpDllHandle := 0;
  4412. SymInitializeAFunc := nil;
  4413. SymInitializeWFunc := nil;
  4414. SymGetOptionsFunc := nil;
  4415. SymSetOptionsFunc := nil;
  4416. SymCleanupFunc := nil;
  4417. SymGetSymFromAddrAFunc := nil;
  4418. SymGetSymFromAddrWFunc := nil;
  4419. SymGetModuleInfoAFunc := nil;
  4420. SymGetModuleInfoWFunc := nil;
  4421. SymLoadModuleFunc := nil;
  4422. SymGetLineFromAddrAFunc := nil;
  4423. SymGetLineFromAddrWFunc := nil;
  4424. end;
  4425. //=== Source location functions ==============================================
  4426. {$STACKFRAMES ON}
  4427. function Caller(Level: Integer; FastStackWalk: Boolean): Pointer;
  4428. var
  4429. TopOfStack: TJclAddr;
  4430. BaseOfStack: TJclAddr;
  4431. StackFrame: PStackFrame;
  4432. begin
  4433. Result := nil;
  4434. try
  4435. if FastStackWalk then
  4436. begin
  4437. StackFrame := GetFramePointer;
  4438. BaseOfStack := TJclAddr(StackFrame) - 1;
  4439. TopOfStack := GetStackTop;
  4440. while (BaseOfStack < TJclAddr(StackFrame)) and (TJclAddr(StackFrame) < TopOfStack) do
  4441. begin
  4442. if Level = 0 then
  4443. begin
  4444. Result := Pointer(StackFrame^.CallerAddr - 1);
  4445. Break;
  4446. end;
  4447. StackFrame := PStackFrame(StackFrame^.CallerFrame);
  4448. Dec(Level);
  4449. end;
  4450. end
  4451. else
  4452. with TJclStackInfoList.Create(False, 1, nil, False, nil, nil) do
  4453. try
  4454. if Level < Count then
  4455. Result := Items[Level].CallerAddr;
  4456. finally
  4457. Free;
  4458. end;
  4459. except
  4460. Result := nil;
  4461. end;
  4462. end;
  4463. {$IFNDEF STACKFRAMES_ON}
  4464. {$STACKFRAMES OFF}
  4465. {$ENDIF ~STACKFRAMES_ON}
  4466. procedure BeginGetLocationInfoCache;
  4467. begin
  4468. BeginModuleFromAddrCache;
  4469. end;
  4470. procedure EndGetLocationInfoCache;
  4471. begin
  4472. EndModuleFromAddrCache;
  4473. end;
  4474. function GetLocationInfo(const Addr: Pointer): TJclLocationInfo;
  4475. begin
  4476. try
  4477. DebugInfoCritSect.Enter;
  4478. try
  4479. NeedDebugInfoList;
  4480. DebugInfoList.GetLocationInfo(Addr, Result)
  4481. finally
  4482. DebugInfoCritSect.Leave;
  4483. end;
  4484. except
  4485. Finalize(Result);
  4486. ResetMemory(Result, SizeOf(Result));
  4487. end;
  4488. end;
  4489. function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;
  4490. begin
  4491. try
  4492. DebugInfoCritSect.Enter;
  4493. try
  4494. NeedDebugInfoList;
  4495. Result := DebugInfoList.GetLocationInfo(Addr, Info);
  4496. finally
  4497. DebugInfoCritSect.Leave;
  4498. end;
  4499. except
  4500. Result := False;
  4501. end;
  4502. end;
  4503. function GetLocationInfoStr(const Addr: Pointer; IncludeModuleName, IncludeAddressOffset,
  4504. IncludeStartProcLineOffset: Boolean; IncludeVAddress: Boolean): string;
  4505. var
  4506. Info, StartProcInfo: TJclLocationInfo;
  4507. OffsetStr, StartProcOffsetStr, FixedProcedureName, UnitNameWithoutUnitscope: string;
  4508. Module: HMODULE;
  4509. begin
  4510. OffsetStr := '';
  4511. if GetLocationInfo(Addr, Info) then
  4512. with Info do
  4513. begin
  4514. FixedProcedureName := ProcedureName;
  4515. if Pos(UnitName + '.', FixedProcedureName) = 1 then
  4516. FixedProcedureName := Copy(FixedProcedureName, Length(UnitName) + 2, Length(FixedProcedureName) - Length(UnitName) - 1)
  4517. else
  4518. if Pos('.', UnitName) > 1 then
  4519. begin
  4520. UnitNameWithoutUnitscope := UnitName;
  4521. Delete(UnitNameWithoutUnitscope, 1, Pos('.', UnitNameWithoutUnitscope));
  4522. if Pos(StrLower(UnitNameWithoutUnitscope) + '.', StrLower(FixedProcedureName)) = 1 then
  4523. FixedProcedureName := Copy(FixedProcedureName, Length(UnitNameWithoutUnitscope) + 2, Length(FixedProcedureName) - Length(UnitNameWithoutUnitscope) - 1);
  4524. end;
  4525. if LineNumber > 0 then
  4526. begin
  4527. if IncludeStartProcLineOffset and GetLocationInfo(Pointer(TJclAddr(Info.Address) -
  4528. Cardinal(Info.OffsetFromProcName)), StartProcInfo) and (StartProcInfo.LineNumber > 0) then
  4529. StartProcOffsetStr := Format(' + %d', [LineNumber - StartProcInfo.LineNumber])
  4530. else
  4531. StartProcOffsetStr := '';
  4532. if IncludeAddressOffset then
  4533. begin
  4534. if OffsetFromLineNumber >= 0 then
  4535. OffsetStr := Format(' + $%x', [OffsetFromLineNumber])
  4536. else
  4537. OffsetStr := Format(' - $%x', [-OffsetFromLineNumber])
  4538. end;
  4539. Result := Format('[%p] %s.%s (Line %u, "%s"%s)%s', [Addr, UnitName, FixedProcedureName, LineNumber,
  4540. SourceName, StartProcOffsetStr, OffsetStr]);
  4541. end
  4542. else
  4543. begin
  4544. if IncludeAddressOffset then
  4545. OffsetStr := Format(' + $%x', [OffsetFromProcName]);
  4546. if UnitName <> '' then
  4547. Result := Format('[%p] %s.%s%s', [Addr, UnitName, FixedProcedureName, OffsetStr])
  4548. else
  4549. Result := Format('[%p] %s%s', [Addr, FixedProcedureName, OffsetStr]);
  4550. end;
  4551. end
  4552. else
  4553. begin
  4554. Result := Format('[%p]', [Addr]);
  4555. IncludeVAddress := True;
  4556. end;
  4557. if IncludeVAddress or IncludeModuleName then
  4558. begin
  4559. Module := ModuleFromAddr(Addr);
  4560. if IncludeVAddress then
  4561. begin
  4562. {$OVERFLOWCHECKS OFF} // Mantis #6104
  4563. OffsetStr := Format('(%p) ', [Pointer(TJclAddr(Addr) - TJclAddr(Module) - ModuleCodeOffset)]);
  4564. {$IFDEF OVERFLOWCHECKS_ON}
  4565. {$OVERFLOWCHECKS ON}
  4566. {$ENDIF OVERFLOWCHECKS_OFF}
  4567. Result := OffsetStr + Result;
  4568. end;
  4569. if IncludeModuleName then
  4570. Insert(Format('{%-12s}', [ExtractFileName(GetModulePath(Module))]), Result, 11 {$IFDEF CPU64}+8{$ENDIF});
  4571. end;
  4572. end;
  4573. function DebugInfoAvailable(const Module: HMODULE): Boolean;
  4574. begin
  4575. DebugInfoCritSect.Enter;
  4576. try
  4577. NeedDebugInfoList;
  4578. Result := (DebugInfoList.ItemFromModule[Module] <> nil);
  4579. finally
  4580. DebugInfoCritSect.Leave;
  4581. end;
  4582. end;
  4583. procedure ClearLocationData;
  4584. begin
  4585. DebugInfoCritSect.Enter;
  4586. try
  4587. if DebugInfoList <> nil then
  4588. DebugInfoList.Clear;
  4589. finally
  4590. DebugInfoCritSect.Leave;
  4591. end;
  4592. end;
  4593. {$STACKFRAMES ON}
  4594. function FileByLevel(const Level: Integer): string;
  4595. begin
  4596. Result := GetLocationInfo(Caller(Level + 1)).SourceName;
  4597. end;
  4598. function ModuleByLevel(const Level: Integer): string;
  4599. begin
  4600. Result := GetLocationInfo(Caller(Level + 1)).UnitName;
  4601. end;
  4602. function ProcByLevel(const Level: Integer; OnlyProcedureName: boolean): string;
  4603. begin
  4604. Result := GetLocationInfo(Caller(Level + 1)).ProcedureName;
  4605. if OnlyProcedureName = true then
  4606. begin
  4607. if StrILastPos('.', Result) > 0 then
  4608. Result :=StrRestOf(Result, StrILastPos('.', Result)+1);
  4609. end;
  4610. end;
  4611. function LineByLevel(const Level: Integer): Integer;
  4612. begin
  4613. Result := GetLocationInfo(Caller(Level + 1)).LineNumber;
  4614. end;
  4615. function MapByLevel(const Level: Integer; var File_, Module_, Proc_: string;
  4616. var Line_: Integer): Boolean;
  4617. begin
  4618. Result := MapOfAddr(Caller(Level + 1), File_, Module_, Proc_, Line_);
  4619. end;
  4620. function ExtractClassName(const ProcedureName: string): string;
  4621. var
  4622. D: Integer;
  4623. begin
  4624. D := Pos('.', ProcedureName);
  4625. if D < 2 then
  4626. Result := ''
  4627. else
  4628. Result := Copy(ProcedureName, 1, D - 1);
  4629. end;
  4630. function ExtractMethodName(const ProcedureName: string): string;
  4631. begin
  4632. Result := Copy(ProcedureName, Pos('.', ProcedureName) + 1, Length(ProcedureName));
  4633. end;
  4634. function __FILE__(const Level: Integer): string;
  4635. begin
  4636. Result := FileByLevel(Level + 1);
  4637. end;
  4638. function __MODULE__(const Level: Integer): string;
  4639. begin
  4640. Result := ModuleByLevel(Level + 1);
  4641. end;
  4642. function __PROC__(const Level: Integer): string;
  4643. begin
  4644. Result := ProcByLevel(Level + 1);
  4645. end;
  4646. function __LINE__(const Level: Integer): Integer;
  4647. begin
  4648. Result := LineByLevel(Level + 1);
  4649. end;
  4650. function __MAP__(const Level: Integer; var _File, _Module, _Proc: string; var _Line: Integer): Boolean;
  4651. begin
  4652. Result := MapByLevel(Level + 1, _File, _Module, _Proc, _Line);
  4653. end;
  4654. {$IFNDEF STACKFRAMES_ON}
  4655. {$STACKFRAMES OFF}
  4656. {$ENDIF ~STACKFRAMES_ON}
  4657. function FileOfAddr(const Addr: Pointer): string;
  4658. begin
  4659. Result := GetLocationInfo(Addr).SourceName;
  4660. end;
  4661. function ModuleOfAddr(const Addr: Pointer): string;
  4662. begin
  4663. Result := GetLocationInfo(Addr).UnitName;
  4664. end;
  4665. function ProcOfAddr(const Addr: Pointer): string;
  4666. begin
  4667. Result := GetLocationInfo(Addr).ProcedureName;
  4668. end;
  4669. function LineOfAddr(const Addr: Pointer): Integer;
  4670. begin
  4671. Result := GetLocationInfo(Addr).LineNumber;
  4672. end;
  4673. function MapOfAddr(const Addr: Pointer; var File_, Module_, Proc_: string;
  4674. var Line_: Integer): Boolean;
  4675. var
  4676. LocInfo: TJclLocationInfo;
  4677. begin
  4678. NeedDebugInfoList;
  4679. Result := DebugInfoList.GetLocationInfo(Addr, LocInfo);
  4680. if Result then
  4681. begin
  4682. File_ := LocInfo.SourceName;
  4683. Module_ := LocInfo.UnitName;
  4684. Proc_ := LocInfo.ProcedureName;
  4685. Line_ := LocInfo.LineNumber;
  4686. end;
  4687. end;
  4688. function __FILE_OF_ADDR__(const Addr: Pointer): string;
  4689. begin
  4690. Result := FileOfAddr(Addr);
  4691. end;
  4692. function __MODULE_OF_ADDR__(const Addr: Pointer): string;
  4693. begin
  4694. Result := ModuleOfAddr(Addr);
  4695. end;
  4696. function __PROC_OF_ADDR__(const Addr: Pointer): string;
  4697. begin
  4698. Result := ProcOfAddr(Addr);
  4699. end;
  4700. function __LINE_OF_ADDR__(const Addr: Pointer): Integer;
  4701. begin
  4702. Result := LineOfAddr(Addr);
  4703. end;
  4704. function __MAP_OF_ADDR__(const Addr: Pointer; var _File, _Module, _Proc: string;
  4705. var _Line: Integer): Boolean;
  4706. begin
  4707. Result := MapOfAddr(Addr, _File, _Module, _Proc, _Line);
  4708. end;
  4709. //=== { TJclStackBaseList } ==================================================
  4710. constructor TJclStackBaseList.Create;
  4711. begin
  4712. inherited Create(True);
  4713. FThreadID := GetCurrentThreadId;
  4714. FTimeStamp := Now;
  4715. end;
  4716. destructor TJclStackBaseList.Destroy;
  4717. begin
  4718. if Assigned(FOnDestroy) then
  4719. FOnDestroy(Self);
  4720. inherited Destroy;
  4721. end;
  4722. //=== { TJclGlobalStackList } ================================================
  4723. type
  4724. TJclStackBaseListClass = class of TJclStackBaseList;
  4725. TJclGlobalStackList = class(TThreadList)
  4726. private
  4727. FLockedTID: DWORD;
  4728. FTIDLocked: Boolean;
  4729. function GetExceptStackInfo(TID: DWORD): TJclStackInfoList;
  4730. function GetLastExceptFrameList(TID: DWORD): TJclExceptFrameList;
  4731. procedure ItemDestroyed(Sender: TObject);
  4732. public
  4733. destructor Destroy; override;
  4734. procedure AddObject(AObject: TJclStackBaseList);
  4735. procedure Clear;
  4736. procedure LockThreadID(TID: DWORD);
  4737. procedure UnlockThreadID;
  4738. function FindObject(TID: DWORD; AClass: TJclStackBaseListClass): TJclStackBaseList;
  4739. property ExceptStackInfo[TID: DWORD]: TJclStackInfoList read GetExceptStackInfo;
  4740. property LastExceptFrameList[TID: DWORD]: TJclExceptFrameList read GetLastExceptFrameList;
  4741. end;
  4742. var
  4743. GlobalStackList: TJclGlobalStackList;
  4744. destructor TJclGlobalStackList.Destroy;
  4745. begin
  4746. with LockList do
  4747. try
  4748. while Count > 0 do
  4749. TObject(Items[0]).Free;
  4750. finally
  4751. UnlockList;
  4752. end;
  4753. inherited Destroy;
  4754. end;
  4755. procedure TJclGlobalStackList.AddObject(AObject: TJclStackBaseList);
  4756. var
  4757. ReplacedObj: TObject;
  4758. begin
  4759. AObject.FOnDestroy := ItemDestroyed;
  4760. with LockList do
  4761. try
  4762. ReplacedObj := FindObject(AObject.ThreadID, TJclStackBaseListClass(AObject.ClassType));
  4763. if ReplacedObj <> nil then
  4764. begin
  4765. Remove(ReplacedObj);
  4766. ReplacedObj.Free;
  4767. end;
  4768. Add(AObject);
  4769. finally
  4770. UnlockList;
  4771. end;
  4772. end;
  4773. procedure TJclGlobalStackList.Clear;
  4774. begin
  4775. with LockList do
  4776. try
  4777. while Count > 0 do
  4778. TObject(Items[0]).Free;
  4779. { The following call to Clear seems to be useless, but it deallocates memory
  4780. by setting the lists capacity back to zero. For the runtime memory leak check
  4781. within DUnit it is important that the allocated memory before and after the
  4782. test is equal. }
  4783. Clear; // do not remove
  4784. finally
  4785. UnlockList;
  4786. end;
  4787. end;
  4788. function TJclGlobalStackList.FindObject(TID: DWORD; AClass: TJclStackBaseListClass): TJclStackBaseList;
  4789. var
  4790. I: Integer;
  4791. Item: TJclStackBaseList;
  4792. begin
  4793. Result := nil;
  4794. with LockList do
  4795. try
  4796. if FTIDLocked and (GetCurrentThreadId = MainThreadID) then
  4797. TID := FLockedTID;
  4798. for I := 0 to Count - 1 do
  4799. begin
  4800. Item := Items[I];
  4801. if (Item.ThreadID = TID) and (Item is AClass) then
  4802. begin
  4803. Result := Item;
  4804. Break;
  4805. end;
  4806. end;
  4807. finally
  4808. UnlockList;
  4809. end;
  4810. end;
  4811. function TJclGlobalStackList.GetExceptStackInfo(TID: DWORD): TJclStackInfoList;
  4812. begin
  4813. Result := TJclStackInfoList(FindObject(TID, TJclStackInfoList));
  4814. end;
  4815. function TJclGlobalStackList.GetLastExceptFrameList(TID: DWORD): TJclExceptFrameList;
  4816. begin
  4817. Result := TJclExceptFrameList(FindObject(TID, TJclExceptFrameList));
  4818. end;
  4819. procedure TJclGlobalStackList.ItemDestroyed(Sender: TObject);
  4820. begin
  4821. with LockList do
  4822. try
  4823. Remove(Sender);
  4824. finally
  4825. UnlockList;
  4826. end;
  4827. end;
  4828. procedure TJclGlobalStackList.LockThreadID(TID: DWORD);
  4829. begin
  4830. with LockList do
  4831. try
  4832. if GetCurrentThreadId = MainThreadID then
  4833. begin
  4834. FTIDLocked := True;
  4835. FLockedTID := TID;
  4836. end
  4837. else
  4838. FTIDLocked := False;
  4839. finally
  4840. UnlockList;
  4841. end;
  4842. end;
  4843. procedure TJclGlobalStackList.UnlockThreadID;
  4844. begin
  4845. with LockList do
  4846. try
  4847. FTIDLocked := False;
  4848. finally
  4849. UnlockList;
  4850. end;
  4851. end;
  4852. //=== { TJclGlobalModulesList } ==============================================
  4853. type
  4854. TJclGlobalModulesList = class(TObject)
  4855. private
  4856. FAddedModules: TStringList;
  4857. FHookedModules: TJclModuleArray;
  4858. FLock: TJclCriticalSection;
  4859. FModulesList: TJclModuleInfoList;
  4860. public
  4861. constructor Create;
  4862. destructor Destroy; override;
  4863. procedure AddModule(const ModuleName: string);
  4864. function CreateModulesList: TJclModuleInfoList;
  4865. procedure FreeModulesList(var ModulesList: TJclModuleInfoList);
  4866. function ValidateAddress(Addr: Pointer): Boolean;
  4867. end;
  4868. var
  4869. GlobalModulesList: TJclGlobalModulesList;
  4870. constructor TJclGlobalModulesList.Create;
  4871. begin
  4872. FLock := TJclCriticalSection.Create;
  4873. end;
  4874. destructor TJclGlobalModulesList.Destroy;
  4875. begin
  4876. FreeAndNil(FLock);
  4877. // Keep FModulesList alive if there are still TJclStackInfoLists referencing it. The
  4878. // last JclStackInfoList will destroy it through FreeModulesList.
  4879. if (FModulesList <> nil) and (FModulesList.FRefCount = 0) then
  4880. FreeAndNil(FModulesList);
  4881. FreeAndNil(FAddedModules);
  4882. inherited Destroy;
  4883. end;
  4884. procedure TJclGlobalModulesList.AddModule(const ModuleName: string);
  4885. var
  4886. IsMultiThreaded: Boolean;
  4887. begin
  4888. IsMultiThreaded := IsMultiThread;
  4889. if IsMultiThreaded then
  4890. FLock.Enter;
  4891. try
  4892. if not Assigned(FAddedModules) then
  4893. begin
  4894. FAddedModules := TStringList.Create;
  4895. FAddedModules.Sorted := True;
  4896. FAddedModules.Duplicates := dupIgnore;
  4897. end;
  4898. FAddedModules.Add(ModuleName);
  4899. finally
  4900. if IsMultiThreaded then
  4901. FLock.Leave;
  4902. end;
  4903. end;
  4904. function TJclGlobalModulesList.CreateModulesList: TJclModuleInfoList;
  4905. var
  4906. I: Integer;
  4907. SystemModulesOnly: Boolean;
  4908. IsMultiThreaded: Boolean;
  4909. AddedModuleHandle: HMODULE;
  4910. begin
  4911. IsMultiThreaded := IsMultiThread;
  4912. if IsMultiThreaded then
  4913. FLock.Enter;
  4914. try
  4915. if FModulesList = nil then
  4916. begin
  4917. SystemModulesOnly := not (stAllModules in JclStackTrackingOptions);
  4918. Result := TJclModuleInfoList.Create(False, SystemModulesOnly);
  4919. // Add known Borland modules collected by DLL exception hooking code
  4920. if SystemModulesOnly and JclHookedExceptModulesList(FHookedModules) then
  4921. for I := Low(FHookedModules) to High(FHookedModules) do
  4922. Result.AddModule(FHookedModules[I], True);
  4923. if Assigned(FAddedModules) then
  4924. for I := 0 to FAddedModules.Count - 1 do
  4925. begin
  4926. AddedModuleHandle := GetModuleHandle(PChar(FAddedModules[I]));
  4927. if (AddedModuleHandle <> 0) and
  4928. not Assigned(Result.ModuleFromAddress[Pointer(AddedModuleHandle)]) then
  4929. Result.AddModule(AddedModuleHandle, True);
  4930. end;
  4931. if stStaticModuleList in JclStackTrackingOptions then
  4932. FModulesList := Result;
  4933. end
  4934. else
  4935. Result := FModulesList;
  4936. finally
  4937. if IsMultiThreaded then
  4938. FLock.Leave;
  4939. end;
  4940. // RefCount the "global" FModulesList so that if GlobalModulesList is destroyed we can keep
  4941. // the FModulesList alive and let it be destroyed by the last TJclStackInfoList.
  4942. if Result = FModulesList then
  4943. InterlockedIncrement(FModulesList.FRefCount);
  4944. end;
  4945. procedure TJclGlobalModulesList.FreeModulesList(var ModulesList: TJclModuleInfoList);
  4946. var
  4947. IsMultiThreaded: Boolean;
  4948. begin
  4949. if Self <> nil then // happens when finalization already ran but a TJclStackInfoList is still alive
  4950. begin
  4951. if FModulesList <> ModulesList then
  4952. begin
  4953. IsMultiThreaded := IsMultiThread;
  4954. if IsMultiThreaded then
  4955. FLock.Enter;
  4956. try
  4957. FreeAndNil(ModulesList);
  4958. finally
  4959. if IsMultiThreaded then
  4960. FLock.Leave;
  4961. end;
  4962. end
  4963. else if FModulesList <> nil then
  4964. InterlockedDecrement(FModulesList.FRefCount);
  4965. end
  4966. else
  4967. if InterlockedDecrement(ModulesList.FRefCount) = 0 then
  4968. FreeAndNil(ModulesList);
  4969. end;
  4970. function TJclGlobalModulesList.ValidateAddress(Addr: Pointer): Boolean;
  4971. var
  4972. TempList: TJclModuleInfoList;
  4973. begin
  4974. TempList := CreateModulesList;
  4975. try
  4976. Result := TempList.IsValidModuleAddress(Addr);
  4977. finally
  4978. FreeModulesList(TempList);
  4979. end;
  4980. end;
  4981. function JclValidateModuleAddress(Addr: Pointer): Boolean;
  4982. begin
  4983. Result := GlobalModulesList.ValidateAddress(Addr);
  4984. end;
  4985. //=== Stack info routines ====================================================
  4986. {$STACKFRAMES OFF}
  4987. function ValidCodeAddr(CodeAddr: DWORD; ModuleList: TJclModuleInfoList): Boolean;
  4988. begin
  4989. if stAllModules in JclStackTrackingOptions then
  4990. Result := ModuleList.IsValidModuleAddress(Pointer(CodeAddr))
  4991. else
  4992. Result := ModuleList.IsSystemModuleAddress(Pointer(CodeAddr));
  4993. end;
  4994. procedure CorrectExceptStackListTop(List: TJclStackInfoList; SkipFirstItem: Boolean);
  4995. var
  4996. TopItem, I, FoundPos: Integer;
  4997. begin
  4998. FoundPos := -1;
  4999. if SkipFirstItem then
  5000. TopItem := 1
  5001. else
  5002. TopItem := 0;
  5003. with List do
  5004. begin
  5005. for I := Count - 1 downto TopItem do
  5006. if JclBelongsHookedCode(Items[I].CallerAddr) then
  5007. begin
  5008. FoundPos := I;
  5009. Break;
  5010. end;
  5011. if FoundPos <> -1 then
  5012. for I := FoundPos downto TopItem do
  5013. Delete(I);
  5014. end;
  5015. end;
  5016. {$STACKFRAMES ON}
  5017. procedure DoExceptionStackTrace(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean;
  5018. BaseOfStack: Pointer);
  5019. var
  5020. IgnoreLevels: Integer;
  5021. FirstCaller: Pointer;
  5022. RawMode: Boolean;
  5023. Delayed: Boolean;
  5024. begin
  5025. RawMode := stRawMode in JclStackTrackingOptions;
  5026. Delayed := stDelayedTrace in JclStackTrackingOptions;
  5027. if BaseOfStack = nil then
  5028. begin
  5029. BaseOfStack := GetFramePointer;
  5030. IgnoreLevels := 1;
  5031. end
  5032. else
  5033. IgnoreLevels := -1; // because of the "IgnoreLevels + 1" in TJclStackInfoList.StoreToList()
  5034. if OSException then
  5035. begin
  5036. if IgnoreLevels = -1 then
  5037. IgnoreLevels := 0
  5038. else
  5039. Inc(IgnoreLevels); // => HandleAnyException
  5040. FirstCaller := ExceptAddr;
  5041. end
  5042. else
  5043. FirstCaller := nil;
  5044. JclCreateStackList(RawMode, IgnoreLevels, FirstCaller, Delayed, BaseOfStack).CorrectOnAccess(OSException);
  5045. end;
  5046. function JclLastExceptStackList: TJclStackInfoList;
  5047. begin
  5048. Result := GlobalStackList.ExceptStackInfo[GetCurrentThreadID];
  5049. end;
  5050. function JclLastExceptStackListToStrings(Strings: TStrings; IncludeModuleName, IncludeAddressOffset,
  5051. IncludeStartProcLineOffset, IncludeVAddress: Boolean): Boolean;
  5052. var
  5053. List: TJclStackInfoList;
  5054. begin
  5055. List := JclLastExceptStackList;
  5056. Result := Assigned(List);
  5057. if Result then
  5058. List.AddToStrings(Strings, IncludeModuleName, IncludeAddressOffset, IncludeStartProcLineOffset,
  5059. IncludeVAddress);
  5060. end;
  5061. function JclGetExceptStackList(ThreadID: DWORD): TJclStackInfoList;
  5062. begin
  5063. Result := GlobalStackList.ExceptStackInfo[ThreadID];
  5064. end;
  5065. function JclGetExceptStackListToStrings(ThreadID: DWORD; Strings: TStrings;
  5066. IncludeModuleName: Boolean = False; IncludeAddressOffset: Boolean = False;
  5067. IncludeStartProcLineOffset: Boolean = False; IncludeVAddress: Boolean = False): Boolean;
  5068. var
  5069. List: TJclStackInfoList;
  5070. begin
  5071. List := JclGetExceptStackList(ThreadID);
  5072. Result := Assigned(List);
  5073. if Result then
  5074. List.AddToStrings(Strings, IncludeModuleName, IncludeAddressOffset, IncludeStartProcLineOffset,
  5075. IncludeVAddress);
  5076. end;
  5077. procedure JclClearGlobalStackData;
  5078. begin
  5079. GlobalStackList.Clear;
  5080. end;
  5081. function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer): TJclStackInfoList;
  5082. begin
  5083. Result := TJclStackInfoList.Create(Raw, AIgnoreLevels, FirstCaller, False, nil, nil);
  5084. GlobalStackList.AddObject(Result);
  5085. end;
  5086. function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;
  5087. DelayedTrace: Boolean): TJclStackInfoList;
  5088. begin
  5089. Result := TJclStackInfoList.Create(Raw, AIgnoreLevels, FirstCaller, DelayedTrace, nil, nil);
  5090. GlobalStackList.AddObject(Result);
  5091. end;
  5092. function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;
  5093. DelayedTrace: Boolean; BaseOfStack: Pointer): TJclStackInfoList;
  5094. begin
  5095. Result := TJclStackInfoList.Create(Raw, AIgnoreLevels, FirstCaller, DelayedTrace, BaseOfStack, nil);
  5096. GlobalStackList.AddObject(Result);
  5097. end;
  5098. function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;
  5099. DelayedTrace: Boolean; BaseOfStack, TopOfStack: Pointer): TJclStackInfoList;
  5100. begin
  5101. Result := TJclStackInfoList.Create(Raw, AIgnoreLevels, FirstCaller, DelayedTrace, BaseOfStack, TopOfStack);
  5102. GlobalStackList.AddObject(Result);
  5103. end;
  5104. function GetThreadTopOfStack(ThreadHandle: THandle): TJclAddr;
  5105. var
  5106. TBI: THREAD_BASIC_INFORMATION;
  5107. ReturnedLength: ULONG;
  5108. begin
  5109. Result := 0;
  5110. ReturnedLength := 0;
  5111. if (NtQueryInformationThread(ThreadHandle, ThreadBasicInformation, @TBI, SizeOf(TBI), @ReturnedLength) < $80000000) and
  5112. (ReturnedLength = SizeOf(TBI)) then
  5113. {$IFDEF CPU32}
  5114. Result := TJclAddr(PNT_TIB32(TBI.TebBaseAddress)^.StackBase)
  5115. {$ENDIF CPU32}
  5116. {$IFDEF CPU64}
  5117. Result := TJclAddr(PNT_TIB64(TBI.TebBaseAddress)^.StackBase)
  5118. {$ENDIF CPU64}
  5119. else
  5120. RaiseLastOSError;
  5121. end;
  5122. function JclCreateThreadStackTrace(Raw: Boolean; const ThreadHandle: THandle): TJclStackInfoList;
  5123. var
  5124. ContextMemory: Pointer;
  5125. AlignedContext: PContext;
  5126. begin
  5127. Result := nil;
  5128. ContextMemory := AllocMem(SizeOf(TContext) + 15);
  5129. try
  5130. if (TJclAddr(ContextMemory) and 15) <> 0 then
  5131. // PAnsiChar: TJclAddr is signed and would cause an int overflow for half the address space
  5132. AlignedContext := PContext(TJclAddr(PAnsiChar(ContextMemory) + 16) and -16)
  5133. else
  5134. AlignedContext := ContextMemory;
  5135. AlignedContext^.ContextFlags := CONTEXT_FULL;
  5136. {$IFDEF CPU32}
  5137. if GetThreadContext(ThreadHandle, AlignedContext^) then
  5138. Result := JclCreateStackList(Raw, -1, Pointer(AlignedContext^.Eip), False, Pointer(AlignedContext^.Ebp),
  5139. Pointer(GetThreadTopOfStack(ThreadHandle)));
  5140. {$ENDIF CPU32}
  5141. {$IFDEF CPU64}
  5142. if GetThreadContext(ThreadHandle, AlignedContext^) then
  5143. Result := JclCreateStackList(Raw, -1, Pointer(AlignedContext^.Rip), False, Pointer(AlignedContext^.Rbp),
  5144. Pointer(GetThreadTopOfStack(ThreadHandle)));
  5145. {$ENDIF CPU64}
  5146. finally
  5147. FreeMem(ContextMemory);
  5148. end;
  5149. end;
  5150. function JclCreateThreadStackTraceFromID(Raw: Boolean; ThreadID: DWORD): TJclStackInfoList;
  5151. type
  5152. TOpenThreadFunc = function(DesiredAccess: DWORD; InheritHandle: BOOL; ThreadID: DWORD): THandle; stdcall;
  5153. const
  5154. THREAD_GET_CONTEXT = $0008;
  5155. THREAD_QUERY_INFORMATION = $0040;
  5156. var
  5157. Kernel32Lib, ThreadHandle: THandle;
  5158. OpenThreadFunc: TOpenThreadFunc;
  5159. begin
  5160. Result := nil;
  5161. Kernel32Lib := GetModuleHandle(kernel32);
  5162. if Kernel32Lib <> 0 then
  5163. begin
  5164. // OpenThread only exists since Windows ME
  5165. OpenThreadFunc := GetProcAddress(Kernel32Lib, 'OpenThread');
  5166. if Assigned(OpenThreadFunc) then
  5167. begin
  5168. ThreadHandle := OpenThreadFunc(THREAD_GET_CONTEXT or THREAD_QUERY_INFORMATION, False, ThreadID);
  5169. if ThreadHandle <> 0 then
  5170. try
  5171. Result := JclCreateThreadStackTrace(Raw, ThreadHandle);
  5172. finally
  5173. CloseHandle(ThreadHandle);
  5174. end;
  5175. end;
  5176. end;
  5177. end;
  5178. //=== { TJclStackInfoItem } ==================================================
  5179. function TJclStackInfoItem.GetCallerAddr: Pointer;
  5180. begin
  5181. Result := Pointer(FStackInfo.CallerAddr);
  5182. end;
  5183. function TJclStackInfoItem.GetLogicalAddress: TJclAddr;
  5184. begin
  5185. Result := FStackInfo.CallerAddr - TJclAddr(ModuleFromAddr(CallerAddr));
  5186. end;
  5187. //=== { TJclStackInfoList } ==================================================
  5188. constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;
  5189. AFirstCaller: Pointer);
  5190. begin
  5191. Create(ARaw, AIgnoreLevels, AFirstCaller, False, nil, nil);
  5192. end;
  5193. constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;
  5194. AFirstCaller: Pointer; ADelayedTrace: Boolean);
  5195. begin
  5196. Create(ARaw, AIgnoreLevels, AFirstCaller, ADelayedTrace, nil, nil);
  5197. end;
  5198. constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;
  5199. AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack: Pointer);
  5200. begin
  5201. Create(ARaw, AIgnoreLevels, AFirstCaller, ADelayedTrace, ABaseOfStack, nil);
  5202. end;
  5203. constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;
  5204. AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack, ATopOfStack: Pointer);
  5205. var
  5206. Item: TJclStackInfoItem;
  5207. begin
  5208. inherited Create;
  5209. InterlockedIncrement(GlobalStackListLiveCount);
  5210. FIgnoreLevels := AIgnoreLevels;
  5211. FDelayedTrace := ADelayedTrace;
  5212. FRaw := ARaw;
  5213. BaseOfStack := TJclAddr(ABaseOfStack);
  5214. FStackOffset := 0;
  5215. FFramePointer := ABaseOfStack;
  5216. if ATopOfStack = nil then
  5217. TopOfStack := GetStackTop
  5218. else
  5219. TopOfStack := TJclAddr(ATopOfStack);
  5220. FModuleInfoList := GlobalModulesList.CreateModulesList;
  5221. if AFirstCaller <> nil then
  5222. begin
  5223. Item := TJclStackInfoItem.Create;
  5224. Item.FStackInfo.CallerAddr := TJclAddr(AFirstCaller);
  5225. Add(Item);
  5226. end;
  5227. {$IFDEF CPU32}
  5228. if DelayedTrace then
  5229. DelayStoreStack
  5230. else
  5231. if Raw then
  5232. TraceStackRaw
  5233. else
  5234. TraceStackFrames;
  5235. {$ENDIF CPU32}
  5236. {$IFDEF CPU64}
  5237. CaptureBackTrace;
  5238. {$ENDIF CPU64}
  5239. end;
  5240. destructor TJclStackInfoList.Destroy;
  5241. begin
  5242. if Assigned(FStackData) then
  5243. FreeMem(FStackData);
  5244. GlobalModulesList.FreeModulesList(FModuleInfoList);
  5245. inherited Destroy;
  5246. if (InterlockedDecrement(GlobalStackListLiveCount) = 0) and JclDebugFinalized then
  5247. FreeJclDebugGlobals;
  5248. end;
  5249. {$IFDEF CPU64}
  5250. procedure TJclStackInfoList.CaptureBackTrace;
  5251. const
  5252. InternalSkipFrames = 1; // skip this method
  5253. var
  5254. BackTrace: array [0..127] of Pointer;
  5255. MaxFrames: Integer;
  5256. Hash: DWORD;
  5257. I: Integer;
  5258. StackInfo: TStackInfo;
  5259. CapturedFramesCount: Word;
  5260. begin
  5261. if JclCheckWinVersion(6, 0) then
  5262. MaxFrames := Length(BackTrace)
  5263. else
  5264. begin
  5265. // For XP and 2003 sum of FramesToSkip and FramesToCapture must be lower than 63
  5266. MaxFrames := 62 - InternalSkipFrames;
  5267. end;
  5268. ResetMemory(BackTrace, SizeOf(BackTrace));
  5269. CapturedFramesCount := CaptureStackBackTrace(InternalSkipFrames, MaxFrames, @BackTrace, Hash);
  5270. ResetMemory(StackInfo, SizeOf(StackInfo));
  5271. for I := 0 to CapturedFramesCount - 1 do
  5272. begin
  5273. StackInfo.CallerAddr := TJclAddr(BackTrace[I]);
  5274. StackInfo.Level := I;
  5275. StoreToList(StackInfo); // skips all frames with a level less than "IgnoreLevels"
  5276. end;
  5277. end;
  5278. {$ENDIF CPU64}
  5279. procedure TJclStackInfoList.ForceStackTracing;
  5280. begin
  5281. if DelayedTrace and Assigned(FStackData) and not FInStackTracing then
  5282. begin
  5283. FInStackTracing := True;
  5284. try
  5285. if Raw then
  5286. TraceStackRaw
  5287. else
  5288. TraceStackFrames;
  5289. if FCorrectOnAccess then
  5290. CorrectExceptStackListTop(Self, FSkipFirstItem);
  5291. finally
  5292. FInStackTracing := False;
  5293. FDelayedTrace := False;
  5294. end;
  5295. end;
  5296. end;
  5297. function TJclStackInfoList.GetCount: Integer;
  5298. begin
  5299. ForceStackTracing;
  5300. Result := inherited Count;
  5301. end;
  5302. procedure TJclStackInfoList.CorrectOnAccess(ASkipFirstItem: Boolean);
  5303. begin
  5304. FCorrectOnAccess := True;
  5305. FSkipFirstItem := ASkipFirstItem;
  5306. end;
  5307. procedure TJclStackInfoList.AddToStrings(Strings: TStrings; IncludeModuleName, IncludeAddressOffset,
  5308. IncludeStartProcLineOffset, IncludeVAddress: Boolean);
  5309. var
  5310. I: Integer;
  5311. begin
  5312. ForceStackTracing;
  5313. Strings.BeginUpdate;
  5314. try
  5315. BeginGetLocationInfoCache;
  5316. try
  5317. for I := 0 to Count - 1 do
  5318. Strings.Add(GetLocationInfoStr(Items[I].CallerAddr, IncludeModuleName, IncludeAddressOffset,
  5319. IncludeStartProcLineOffset, IncludeVAddress));
  5320. finally
  5321. EndGetLocationInfoCache;
  5322. end;
  5323. finally
  5324. Strings.EndUpdate;
  5325. end;
  5326. end;
  5327. function TJclStackInfoList.GetItems(Index: Integer): TJclStackInfoItem;
  5328. begin
  5329. ForceStackTracing;
  5330. Result := TJclStackInfoItem(Get(Index));
  5331. end;
  5332. function TJclStackInfoList.NextStackFrame(var StackFrame: PStackFrame; var StackInfo: TStackInfo): Boolean;
  5333. var
  5334. CallInstructionSize: Cardinal;
  5335. StackFrameCallerFrame, NewFrame: TJclAddr;
  5336. StackFrameCallerAddr: TJclAddr;
  5337. begin
  5338. // Only report this stack frame into the StockInfo structure
  5339. // if the StackFrame pointer, the frame pointer and the return address on the stack
  5340. // are valid addresses
  5341. StackFrameCallerFrame := StackInfo.CallerFrame;
  5342. while ValidStackAddr(TJclAddr(StackFrame)) do
  5343. begin
  5344. // CallersEBP above the previous CallersEBP
  5345. NewFrame := StackFrame^.CallerFrame;
  5346. if NewFrame <= StackFrameCallerFrame then
  5347. Break;
  5348. StackFrameCallerFrame := NewFrame;
  5349. // CallerAddr within current process space, code segment etc.
  5350. // CallerFrame within current thread stack. Added Mar 12 2002 per Hallvard's suggestion
  5351. StackFrameCallerAddr := StackFrame^.CallerAddr;
  5352. if ValidCodeAddr(StackFrameCallerAddr, FModuleInfoList) and ValidStackAddr(StackFrameCallerFrame + FStackOffset) then
  5353. begin
  5354. Inc(StackInfo.Level);
  5355. StackInfo.StackFrame := StackFrame;
  5356. StackInfo.ParamPtr := PDWORD_PTRArray(TJclAddr(StackFrame) + SizeOf(TStackFrame));
  5357. if StackFrameCallerFrame > StackInfo.CallerFrame then
  5358. StackInfo.CallerFrame := StackFrameCallerFrame
  5359. else
  5360. // the frame pointer points to an address that is below
  5361. // the last frame pointer, so it must be invalid
  5362. Break;
  5363. // Calculate the address of caller by subtracting the CALL instruction size (if possible)
  5364. if ValidCallSite(StackFrameCallerAddr, CallInstructionSize) then
  5365. StackInfo.CallerAddr := StackFrameCallerAddr - CallInstructionSize
  5366. else
  5367. StackInfo.CallerAddr := StackFrameCallerAddr;
  5368. // the stack may be messed up in big projects, avoid overflow in arithmetics
  5369. if StackFrameCallerFrame + FStackOffset < TJclAddr(StackFrame) then
  5370. Break;
  5371. StackInfo.DumpSize := StackFrameCallerFrame + FStackOffset - TJclAddr(StackFrame);
  5372. StackInfo.ParamSize := (StackInfo.DumpSize - SizeOf(TStackFrame)) div 4;
  5373. if PStackFrame(StackFrame^.CallerFrame + FStackOffset) = StackFrame then
  5374. Break;
  5375. // Step to the next stack frame by following the frame pointer
  5376. StackFrame := PStackFrame(StackFrameCallerFrame + FStackOffset);
  5377. Result := True;
  5378. Exit;
  5379. end;
  5380. // Step to the next stack frame by following the frame pointer
  5381. StackFrame := PStackFrame(StackFrameCallerFrame + FStackOffset);
  5382. end;
  5383. Result := False;
  5384. end;
  5385. procedure TJclStackInfoList.StoreToList(const StackInfo: TStackInfo);
  5386. var
  5387. Item: TJclStackInfoItem;
  5388. begin
  5389. if ((IgnoreLevels = -1) and (StackInfo.Level > 0)) or
  5390. (StackInfo.Level > (IgnoreLevels + 1)) then
  5391. begin
  5392. Item := TJclStackInfoItem.Create;
  5393. Item.FStackInfo := StackInfo;
  5394. Add(Item);
  5395. end;
  5396. end;
  5397. procedure TJclStackInfoList.TraceStackFrames;
  5398. var
  5399. StackFrame: PStackFrame;
  5400. StackInfo: TStackInfo;
  5401. begin
  5402. Capacity := 32; // reduce ReallocMem calls, must be > 1 because the caller's EIP register is already in the list
  5403. // Start at level 0
  5404. StackInfo.Level := 0;
  5405. StackInfo.CallerFrame := 0;
  5406. if DelayedTrace then
  5407. // Get the current stack frame from the frame register
  5408. StackFrame := FFramePointer
  5409. else
  5410. begin
  5411. // We define the bottom of the valid stack to be the current ESP pointer
  5412. if BaseOfStack = 0 then
  5413. BaseOfStack := TJclAddr(GetFramePointer);
  5414. // Get a pointer to the current bottom of the stack
  5415. StackFrame := PStackFrame(BaseOfStack);
  5416. end;
  5417. // We define the bottom of the valid stack to be the current frame Pointer
  5418. // There is a TIB field called pvStackUserBase, but this includes more of the
  5419. // stack than what would define valid stack frames.
  5420. BaseOfStack := TJclAddr(StackFrame) - 1;
  5421. // Loop over and report all valid stackframes
  5422. while NextStackFrame(StackFrame, StackInfo) and (inherited Count <> MaxStackTraceItems) do
  5423. StoreToList(StackInfo);
  5424. end;
  5425. function TraceStackInstuctions(Proc, InstructionAddr: Pointer; ModuleEndAddr: TJclAddr;
  5426. var LocalVarStackOffset, ParamStackOffset: Integer): Boolean;
  5427. const
  5428. PointerSize = SizeOf(Pointer);
  5429. function ParseSaveRegisters(ProcAddr, CallAddr: TJclAddr; var RegisterStackOffset: Integer): TJclAddr;
  5430. var
  5431. P: PByteArray;
  5432. begin
  5433. Result := ProcAddr;
  5434. while Result < TJclAddr(CallAddr) do
  5435. begin
  5436. P := PByteArray(Result);
  5437. if (P[0] and $F8) = $50 then // PUSH r32
  5438. begin
  5439. Inc(RegisterStackOffset, PointerSize);
  5440. Inc(Result);
  5441. Continue;
  5442. end;
  5443. Break;
  5444. end;
  5445. end;
  5446. function CheckRegisterRestoreBackwards(ProcAddr, CallAddr: TJclAddr; var RegisterStackOffset: Integer): Boolean;
  5447. var
  5448. Count: Integer;
  5449. begin
  5450. if RegisterStackOffset > 0 then
  5451. begin
  5452. Count := 0;
  5453. while (ProcAddr > CallAddr) and (PByte(ProcAddr)^ and $F8 = $58) do // POP r32
  5454. begin
  5455. Dec(ProcAddr);
  5456. Inc(Count);
  5457. end;
  5458. if (Count > 0) and (Cardinal(Count) <= Cardinal(RegisterStackOffset) div PointerSize) then
  5459. begin
  5460. // We may have used a "function call push" in the prolog analysis so fix this
  5461. RegisterStackOffset := Count * PointerSize;
  5462. Result := True;
  5463. end
  5464. else
  5465. Result := False;
  5466. end
  5467. else
  5468. Result := True;
  5469. end;
  5470. function ParseEspChange(ProcAddr, CallAddr: TJclAddr; var LocalVarStackOffset: Integer; var EspChangeFound: Boolean): TJclAddr;
  5471. var
  5472. P: PByteArray;
  5473. begin
  5474. Result := ProcAddr;
  5475. P := PByteArray(Result);
  5476. if (Result + 3 < TJclAddr(CallAddr)) and (P[0] = $83) and (P[1] = $C4) then // 83C4F8 add esp,imm8
  5477. begin
  5478. Inc(LocalVarStackOffset, -Integer(ShortInt(P[2])));
  5479. EspChangeFound := True;
  5480. Inc(Result, 3);
  5481. end
  5482. else if (Result + 6 < TJclAddr(CallAddr)) and (P[0] = $81) and (P[1] = $C4) then // 81C408000100 add esp,imm32
  5483. begin
  5484. Inc(LocalVarStackOffset, -PInteger(@P[2])^);
  5485. EspChangeFound := True;
  5486. Inc(Result, 6);
  5487. end;
  5488. end;
  5489. function CheckEspChangeBackwards(ProcAddr, CallAddr: TJclAddr): Boolean;
  5490. var
  5491. Offset: Integer;
  5492. begin
  5493. Inc(ProcAddr);
  5494. Result := False;
  5495. if ProcAddr - 3 >= CallAddr then
  5496. begin
  5497. ParseEspChange(ProcAddr - 3, ProcAddr + 1, Offset, Result);
  5498. if Result then
  5499. Exit;
  5500. end;
  5501. if ProcAddr - 6 >= CallAddr then
  5502. begin
  5503. ParseEspChange(ProcAddr - 6, ProcAddr + 1, Offset, Result);
  5504. if Result then
  5505. Exit;
  5506. end;
  5507. end;
  5508. function CheckStackAddressValidation(ProcAddr, CallAddr: TJclAddr; var LocalVarStackOffset: Integer;
  5509. var EspChangeFound: Boolean): Integer;
  5510. var
  5511. P: PByteArray;
  5512. begin
  5513. // The compiler emits multiple functino prologues to probe the stack frame memory pages.
  5514. P := PByteArray(ProcAddr);
  5515. if (ProcAddr + 6 < CallAddr) and
  5516. (P[0] = $81) and (P[1] = $C4) and (PInteger(@P[2])^ = -4092) and // 81C404F0FFFF add esp,$fffff004
  5517. (P[6] = $50) then // 50 push eax
  5518. begin
  5519. Inc(LocalVarStackOffset, (4092+4));
  5520. EspChangeFound := True;
  5521. Result := 7;
  5522. end
  5523. else if (ProcAddr + 8 < CallAddr) and // CompilerSpeedPack option -x-fpr
  5524. (P[0] = $81) and (P[1] = $C4) and (PInteger(@P[2])^ = -4096) and // 81C404F0FFFF add esp,$fffff000
  5525. (P[6] = $85) and (P[7] = $24) and (P[8] = $24) then // 852424 test [esp],esp
  5526. begin
  5527. Inc(LocalVarStackOffset, 4096);
  5528. EspChangeFound := True;
  5529. Result := 9;
  5530. end
  5531. else if (ProcAddr + 17 + 4 < CallAddr) and
  5532. (P[0] = $50) and // 50 push eax
  5533. (P[1] = $B8) and // B804000000 mov eax,imm32
  5534. (P[6] = $81) and (P[7] = $C4) and (PInteger(@P[8])^ = -4092) and // 81C404F0FFFF add esp,$fffff004
  5535. (P[12] = $50) and // 50 push eax
  5536. (P[13] = $48) and // 48 dec eax
  5537. (P[14] = $75) and (P[15] = $F6) and // 75F6 jnz -10
  5538. (P[16] = $8B) and ((PWord(@P[16])^ = $2484) or (P[17] = $45)) then // 8B842400000100 mov eax,[esp+imm32] / 8B45FC mov eax,[ebp-imm8]
  5539. begin
  5540. Inc(LocalVarStackOffset, PInteger(@P[2])^ * (4092+4));
  5541. EspChangeFound := True;
  5542. Result := 19;
  5543. if P[17] = $45 then
  5544. Inc(Result, 1) // 8B45FC mov eax,[ebp-imm8]
  5545. else
  5546. Inc(Result, 4); // 8B842400000100 mov eax,[esp+imm32]
  5547. end
  5548. else if (ProcAddr + 20 + 4 < CallAddr) and // CompilerSpeedPack option -x-fpr
  5549. (P[0] = $50) and // 50 push eax
  5550. (P[1] = $B8) and // B804000000 mov eax,imm32
  5551. (P[6] = $81) and (P[7] = $C4) and (PInteger(@P[8])^ = -4096) and // 81C404F0FFFF add esp,$fffff000
  5552. (P[12] = $85) and (P[13] = $24) and (P[14] = $24) and // 852424 test [esp],esp
  5553. (P[15] = $48) and // 48 dec eax
  5554. (P[16] = $75) and (P[17] = $F6) and // 75F6 jnz -10
  5555. (P[18] = $8B) and ((PWord(@P[19])^ = $2484) or (P[19] = $45)) then // 8B842400000100 mov eax,[esp+imm32] / 8B45FC mov eax,[ebp-imm8]
  5556. begin
  5557. Inc(LocalVarStackOffset, PInteger(@P[2])^ * 4096);
  5558. EspChangeFound := True;
  5559. Result := 21;
  5560. if P[19] = $45 then
  5561. Inc(Result, 1) // 8B45FC mov eax,[ebp-imm8]
  5562. else
  5563. Inc(Result, 4); // 8B842400000100 mov eax,[esp+imm32]
  5564. end
  5565. else if (ProcAddr + 2 < CallAddr) and
  5566. (P[0] = $33) and (P[1] = $C9) and // 33C9 xor ecx,ecx
  5567. (P[2] = $51) then // 51 push ecx
  5568. begin
  5569. Inc(LocalVarStackOffset, 4);
  5570. EspChangeFound := True;
  5571. Result := 1;
  5572. Inc(ProcAddr, 3);
  5573. while (ProcAddr + 2 < CallAddr) and (PByte(ProcAddr)^ = $51) do
  5574. begin
  5575. Inc(ProcAddr);
  5576. Inc(Result);
  5577. end;
  5578. Inc(LocalVarStackOffset, 4 * Result);
  5579. Inc(Result, 2); // xor ecx, ecx
  5580. end
  5581. // Compiler sets the stack for managed local variables to zero
  5582. else if (ProcAddr + 12 < CallAddr) and
  5583. (P[0] = $51) and // 51 push ecx
  5584. (P[1] = $B9) and // imm32 // B906000000 mov ecx,imm32
  5585. (P[6] = $6A) and (P[7] = $00) and // 6A00 push $00
  5586. (P[8] = $6A) and (P[9] = $00) and // 6A00 push $00
  5587. (P[10] = $49) and // 49 dec ecx
  5588. (P[11] = $75) and (P[12] = $F9) then // 75F9 jnz -7
  5589. begin
  5590. Inc(LocalVarStackOffset, PInteger(@P[2])^ * PointerSize * 2);
  5591. EspChangeFound := True;
  5592. Result := 13;
  5593. // For an odd number of local DWORDs the compiler emits an additional "push ecx"
  5594. if (ProcAddr + 13 < CallAddr) and
  5595. (P[13] = $51) then // 51 push ecx
  5596. begin
  5597. Inc(LocalVarStackOffset, PointerSize);
  5598. Inc(Result, 1);
  5599. end;
  5600. if (ProcAddr + TJclAddr(Result) + 3 < CallAddr) and
  5601. (P[Result + 0] = $87) and (P[Result + 1] = $4D) then // imm8 // 874DFC xchg [ebp-imm8],ecx
  5602. begin
  5603. Inc(Result, 3);
  5604. end
  5605. else if (ProcAddr + TJclAddr(Result) + 10 < CallAddr) and // CompilerSpeedPack option -x-fpr
  5606. (P[Result + 0] = $8B) and (P[Result + 1] = $4D) and //imm8 // 8B4DFC mov ecx,[ebp-imm8]
  5607. (P[Result + 3] = $C7) and (P[Result + 4] = $45) and (P[Result + 5] = $FC) and // C745FC00000000 mov [ebp-$04],$00000000
  5608. (PInteger(@P[Result + 6])^ = 0) then
  5609. begin
  5610. Inc(Result, 10);
  5611. end;
  5612. end
  5613. else
  5614. Result := 0;
  5615. end;
  5616. var
  5617. P: PByteArray;
  5618. ProcAddr, CallAddr, EpilogAddr: TJclAddr;
  5619. StackFrameFound: Integer;
  5620. RegisterStackOffset: Integer;
  5621. EspChangeFound: Boolean;
  5622. Size: Integer;
  5623. PossibleEndFound: Boolean;
  5624. EpilogInfo: TJclLocationInfo;
  5625. RegStackOffset: Integer;
  5626. begin
  5627. LocalVarStackOffset := 0;
  5628. ParamStackOffset := 0;
  5629. RegisterStackOffset := 0;
  5630. Result := False;
  5631. if Proc = nil then
  5632. Exit;
  5633. ProcAddr := TJclAddr(Proc);
  5634. CallAddr := TJclAddr(InstructionAddr);
  5635. // Prolog: stackframe
  5636. StackFrameFound := 0;
  5637. EspChangeFound := False;
  5638. if ProcAddr < CallAddr then
  5639. begin
  5640. P := PByteArray(ProcAddr);
  5641. if (P[0] = $55) and // PUSH EBP
  5642. (P[1] = $8B) and (P[2] = $EC) then // MOV EBP,ESP
  5643. begin
  5644. LocalVarStackOffset := PointerSize; // EBP
  5645. StackFrameFound := 1; // Epilog must end with "POP EBP"
  5646. Inc(ProcAddr, 3);
  5647. end
  5648. else if (P[0] = $C8) and (ProcAddr + 4 < CallAddr) then // ENTER Size(Word), NestingLevel(Byte)
  5649. begin
  5650. LocalVarStackOffset := PointerSize + PWord(@P[1])^ + PointerSize*P[3]; // EBP + Size + 4*NestingLevel
  5651. StackFrameFound := -1; // Epilog must end with "LEAVE"
  5652. Inc(ProcAddr, 4);
  5653. end;
  5654. end;
  5655. if StackFrameFound = 0 then
  5656. begin
  5657. // Prolog: save registers
  5658. // If we have no stackframe, then the compiler saves the registers before allocating stack variables.
  5659. // RegisterStackOffset is preliminary because it may be reset by Epilog's POP code that is more
  5660. // accurate because we can't distinguish between the save register and an immediatelly following
  5661. // function parameter "PUSH".
  5662. ProcAddr := ParseSaveRegisters(ProcAddr, CallAddr, {var} RegisterStackOffset);
  5663. // Prolog: no stackframe + stack address validation
  5664. Size := 0;
  5665. if RegisterStackOffset >= PointerSize then
  5666. begin
  5667. // If there is a "push eax", then the ParseSaveRegisters handled it, but it may be the
  5668. // stack validation's "push eax".
  5669. Size := CheckStackAddressValidation(ProcAddr - 1{push eax}, CallAddr, {var} LocalVarStackOffset, {var} EspChangeFound);
  5670. if Size > 0 then
  5671. begin
  5672. Dec(RegisterStackOffset, PointerSize);
  5673. Dec(ProcAddr);
  5674. end;
  5675. end;
  5676. if Size = 0 then
  5677. Size := CheckStackAddressValidation(ProcAddr, CallAddr, {var} LocalVarStackOffset, {var} EspChangeFound);
  5678. Inc(ProcAddr, Size);
  5679. ProcAddr := ParseEspChange(ProcAddr, CallAddr, LocalVarStackOffset, {var} EspChangeFound);
  5680. end
  5681. else
  5682. begin
  5683. // Prolog: stackframe + stack address validation
  5684. Size := CheckStackAddressValidation(ProcAddr, CallAddr, {var} LocalVarStackOffset, {var} EspChangeFound);
  5685. Inc(ProcAddr, Size);
  5686. ProcAddr := ParseEspChange(ProcAddr, CallAddr, LocalVarStackOffset, {var} EspChangeFound);
  5687. // If we have a stackframe, then the compiler saves the registers after allocating stack variables.
  5688. ProcAddr := ParseSaveRegisters(ProcAddr, CallAddr, {var} RegisterStackOffset);
  5689. end;
  5690. // Find not closed try/finally/except blocks and add them the LocalVarStackOffset
  5691. while (ProcAddr < CallAddr) and (ProcAddr < ModuleEndAddr) do
  5692. begin
  5693. // fast forward find for XOR EAX,EAX
  5694. while (ProcAddr < CallAddr) and (ProcAddr < ModuleEndAddr) and (PByteArray(ProcAddr)[0] <> $33) do
  5695. Inc(ProcAddr);
  5696. P := PByteArray(ProcAddr);
  5697. // Find all occurrences above the CallAddr and add to LocalVarStackOffset (3*PointerSize)
  5698. // "try"
  5699. // 33C0 xor eax,eax
  5700. // 55 push ebp
  5701. // 68E9E05000 push $0050e0e9
  5702. // 64FF30 push dword ptr fs:[eax]
  5703. // 648920 mov fs:[eax],esp
  5704. if (ProcAddr + 13 < CallAddr) and
  5705. (P[0] = $33) and (P[1] = $C0) and
  5706. (P[2] = $55) and
  5707. (P[3] = $68) and
  5708. (P[8] = $64) and (P[9] = $FF) and (P[10] = $30) and
  5709. (P[11] = $64) and (P[12] = $89) and (P[13] = $20) then
  5710. begin
  5711. Inc(LocalVarStackOffset, 3 * PointerSize);
  5712. end
  5713. // "finally"/"except"
  5714. // Find all occurrences above the CallAddr and substract from LocalVarStackOffset (3*PointerSize)
  5715. // 33C0 xor eax,eax
  5716. // 5A pop edx
  5717. // 59 pop ecx
  5718. // 59 pop ecx
  5719. // 648910 mov fs:[eax],edx
  5720. else if (ProcAddr + 7 < CallAddr) and
  5721. (P[0] = $33) and (P[1] = $C0) and
  5722. (P[2] = $5A) and
  5723. (P[3] = $59) and
  5724. (P[4] = $59) and
  5725. (P[5] = $64) and (P[6] = $89) and (P[7] = $10) then
  5726. begin
  5727. Dec(LocalVarStackOffset, 3 * PointerSize);
  5728. end;
  5729. Inc(ProcAddr);
  5730. end;
  5731. // Find the epilog to obtain the ParamStackOffset (would be much easier and less guess work
  5732. // if we knew the exact function's end address)
  5733. ProcAddr := CallAddr;
  5734. while ProcAddr < ModuleEndAddr do
  5735. begin
  5736. // fast forward find for RET / RET imm16
  5737. while (ProcAddr < ModuleEndAddr) and not (PByteArray(ProcAddr)[0] in [$C3, $C2]) do
  5738. Inc(ProcAddr);
  5739. P := PByteArray(ProcAddr);
  5740. // We may have found the RET of a finally clause
  5741. if (ProcAddr + 7 < ModuleEndAddr) and // skip "finally" code
  5742. (P[0] = $C3) and // C3 ret
  5743. (P[1] = $E9) and // E91821FAFF jmp @HandleFinally
  5744. (P[6] = $EB) and (ShortInt(P[7]) < 0) then // EBF8 jmp imm8
  5745. begin
  5746. Inc(ProcAddr, 8);
  5747. end
  5748. else if (ProcAddr + 10 < ModuleEndAddr) and // skip "finally" code
  5749. (P[0] = $C3) and // C3 ret
  5750. (P[1] = $E9) and // E91821FAFF jmp @HandleFinally
  5751. (P[6] = $E9) and (PInteger(@P[7])^ < 0) then // E9xxxxxxxx jmp imm32
  5752. begin
  5753. Inc(ProcAddr, 11);
  5754. end
  5755. else if (P[0] = $C3) or ((P[0] = $C2) and (ProcAddr + 3 < ModuleEndAddr)) then
  5756. begin
  5757. EpilogAddr := ProcAddr;
  5758. PossibleEndFound := False;
  5759. if StackFrameFound = 1 then
  5760. begin
  5761. // If we have a stackframe, then we verify that the stackframe is cleared to check
  5762. // if we found a valid "RET"
  5763. if EspChangeFound then
  5764. EpilogAddr := EpilogAddr - 3
  5765. else
  5766. EpilogAddr := EpilogAddr - 1;
  5767. if EpilogAddr >= CallAddr then
  5768. begin
  5769. P := PByteArray(EpilogAddr);
  5770. if EspChangeFound and
  5771. (P[0] = $8B) and (P[1] = $E5) and // 8BE5 mov esp,ebp
  5772. (P[2] = $5D) then // 5D pop ebp
  5773. begin
  5774. Dec(EpilogAddr);
  5775. PossibleEndFound := True;
  5776. end
  5777. else if not EspChangeFound and
  5778. (P[0] = $5D) then // 5D pop ebp
  5779. begin
  5780. Dec(EpilogAddr);
  5781. PossibleEndFound := True;
  5782. end;
  5783. end;
  5784. end
  5785. else if StackFrameFound = -1 then
  5786. begin
  5787. // If we have a ENTER/LEAVE stackframe, then we verify that the stackframe is cleared
  5788. // to check if we found a valid "RET"
  5789. Dec(EpilogAddr);
  5790. P := PByteArray(EpilogAddr);
  5791. if (EpilogAddr >= CallAddr) and (P[0] = $C9) then // LEAVE
  5792. begin
  5793. Dec(EpilogAddr);
  5794. PossibleEndFound := True;
  5795. end;
  5796. end
  5797. else
  5798. begin
  5799. // If we have no stackframe, then we can't verify the validity of the "RET" here
  5800. EpilogAddr := EpilogAddr - 1;
  5801. PossibleEndFound := True;
  5802. end;
  5803. if PossibleEndFound then
  5804. begin
  5805. if GetLocationInfo(Pointer(EpilogAddr), EpilogInfo) and
  5806. (TJclAddr(EpilogInfo.OffsetFromProcName) <> EpilogAddr - TJclAddr(Proc)) then
  5807. begin
  5808. // If we didn't find a RET in the same procedure then the analysis failed
  5809. Exit;
  5810. end;
  5811. if PossibleEndFound then
  5812. begin
  5813. // If we have registers saved on the stack, we can use those to verify if the
  5814. // found "RET" is valid.
  5815. RegStackOffset := RegisterStackOffset;
  5816. if CheckRegisterRestoreBackwards(EpilogAddr, CallAddr, {var} RegStackOffset) then
  5817. begin
  5818. if (StackFrameFound = 0) and EspChangeFound then
  5819. begin
  5820. // If we have local variables (ESP was changed in the prolog) we can use that
  5821. // information to verify the "RET"
  5822. EpilogAddr := EpilogAddr - TJclAddr(RegStackOffset) div PointerSize;
  5823. if not CheckEspChangeBackwards(EpilogAddr, CallAddr) then
  5824. PossibleEndFound := False;
  5825. end;
  5826. if PossibleEndFound then
  5827. begin
  5828. RegisterStackOffset := RegStackOffset;
  5829. if PByte(ProcAddr)^ = $C2 then
  5830. ParamStackOffset := PWord(ProcAddr + 1)^
  5831. else
  5832. begin
  5833. // TODO: if we only have a "RET" at the end we need to look at the call instruction
  5834. // if it is followed by a "sub/add esp,xx" for a "cdecl" function. (What if the add/sub
  5835. // is for the caller's epilog?)
  5836. end;
  5837. Break;
  5838. end;
  5839. end;
  5840. end;
  5841. end;
  5842. end;
  5843. Inc(ProcAddr);
  5844. end;
  5845. Inc(LocalVarStackOffset, RegisterStackOffset);
  5846. Result := True;
  5847. end;
  5848. procedure TJclStackInfoList.TraceStackRaw;
  5849. var
  5850. StackInfo: TStackInfo;
  5851. StackPtr: PJclAddr;
  5852. PrevCaller: TJclAddr;
  5853. CallInstructionSize: Cardinal;
  5854. StackTop: TJclAddr;
  5855. ProcInfo: TJclLocationInfo;
  5856. ProcStart: Pointer;
  5857. CallInstructionPtr: Pointer;
  5858. LocalVarStackOffset, ParamStackOffset: Integer;
  5859. ModuleEndAddr: TJclAddr;
  5860. begin
  5861. Capacity := 32; // reduce ReallocMem calls, must be > 1 because the caller's EIP register is already in the list
  5862. if DelayedTrace then
  5863. begin
  5864. if not Assigned(FStackData) then
  5865. Exit;
  5866. StackPtr := PJclAddr(FStackData);
  5867. end
  5868. else
  5869. begin
  5870. // We define the bottom of the valid stack to be the current ESP pointer
  5871. if BaseOfStack = 0 then
  5872. BaseOfStack := TJclAddr(GetStackPointer);
  5873. // Get a pointer to the current bottom of the stack
  5874. StackPtr := PJclAddr(BaseOfStack);
  5875. end;
  5876. StackTop := TopOfStack;
  5877. // We will not be able to fill in all the fields in the StackInfo record,
  5878. // so just blank it all out first
  5879. ResetMemory(StackInfo, SizeOf(StackInfo));
  5880. // Clear the previous call address
  5881. PrevCaller := 0;
  5882. // stCleanRawStack: We don't know the number of parameters for the "initial" function
  5883. ParamStackOffset := 0;
  5884. if stCleanRawStack in JclStackTrackingOptions then
  5885. BeginGetLocationInfoCache; // speed up the GetLocationInfo calls
  5886. // Loop through all of the valid stack space
  5887. try
  5888. while (TJclAddr(StackPtr) < StackTop) and (inherited Count <> MaxStackTraceItems) do
  5889. begin
  5890. // If the current DWORD on the stack refers to a valid call site...
  5891. if ValidCallSite(StackPtr^, CallInstructionSize) and (StackPtr^ <> PrevCaller) then
  5892. begin
  5893. // then pick up the callers address
  5894. StackInfo.CallerAddr := StackPtr^ - CallInstructionSize;
  5895. // remember to callers address so that we don't report it repeatedly
  5896. PrevCaller := StackPtr^;
  5897. // increase the stack level
  5898. Inc(StackInfo.Level);
  5899. // then report it back to our caller
  5900. StoreToList(StackInfo);
  5901. if stCleanRawStack in JclStackTrackingOptions then
  5902. begin
  5903. // Skip all stack parameters of the last called function
  5904. Inc(PByte(StackPtr), ParamStackOffset);
  5905. ParamStackOffset := 0;
  5906. CallInstructionPtr := Pointer(StackInfo.CallerAddr);
  5907. if GetLocationInfo(CallInstructionPtr, ProcInfo) then
  5908. begin
  5909. if ProcInfo.ProcedureName <> '' then
  5910. begin
  5911. if (ProcInfo.ProcedureName[1] = '@') and (ProcInfo.ProcedureName = '@RaiseExcept$qqrv') then
  5912. begin
  5913. // Special handling for _RaiseExcept because it does a lot to the stack including
  5914. // putting the ExceptAddr multiple times on the stack causing TraceStackInstuctions to
  5915. // change the StackPtr to the wrong locations.
  5916. LocalVarStackOffset := 17 * SizeOf(Pointer);
  5917. ParamStackOffset := 6 * SizeOf(Pointer);
  5918. Inc(PByte(StackPtr), LocalVarStackOffset);
  5919. end
  5920. else
  5921. begin
  5922. ProcStart := Pointer(TJclAddr(CallInstructionPtr) - TJclAddr(ProcInfo.OffsetFromProcName));
  5923. ModuleEndAddr := TJclAddr(ProcInfo.DebugInfo.Module) + ModuleCodeOffset + TJclAddr(ProcInfo.DebugInfo.ModuleCodeSize);
  5924. if TraceStackInstuctions(ProcStart, CallInstructionPtr, ModuleEndAddr, LocalVarStackOffset, ParamStackOffset) then
  5925. Inc(PByte(StackPtr), LocalVarStackOffset) // skip all local variables (and saved registers)
  5926. else
  5927. ParamStackOffset := 0; // Don't skip stack entries if TraceStackInstuctions failed
  5928. end;
  5929. end;
  5930. end;
  5931. end;
  5932. end;
  5933. // Look at the next DWORD on the stack
  5934. Inc(StackPtr);
  5935. end;
  5936. finally
  5937. if stCleanRawStack in JclStackTrackingOptions then
  5938. EndGetLocationInfoCache;
  5939. if Assigned(FStackData) then
  5940. begin
  5941. FreeMem(FStackData);
  5942. FStackData := nil;
  5943. end;
  5944. end;
  5945. end;
  5946. {$IFDEF CPU32}
  5947. procedure TJclStackInfoList.DelayStoreStack;
  5948. var
  5949. StackPtr: PJclAddr;
  5950. StackDataSize: Cardinal;
  5951. begin
  5952. if Assigned(FStackData) then
  5953. begin
  5954. FreeMem(FStackData);
  5955. FStackData := nil;
  5956. end;
  5957. // We define the bottom of the valid stack to be the current ESP pointer
  5958. if BaseOfStack = 0 then
  5959. begin
  5960. BaseOfStack := TJclAddr(GetStackPointer);
  5961. FFramePointer := GetFramePointer;
  5962. end;
  5963. // Get a pointer to the current bottom of the stack
  5964. StackPtr := PJclAddr(BaseOfStack);
  5965. if TJclAddr(StackPtr) < TopOfStack then
  5966. begin
  5967. StackDataSize := TopOfStack - TJclAddr(StackPtr);
  5968. GetMem(FStackData, StackDataSize);
  5969. System.Move(StackPtr^, FStackData^, StackDataSize);
  5970. end;
  5971. FStackOffset := Int64(FStackData) - Int64(StackPtr);
  5972. FFramePointer := Pointer(TJclAddr(FFramePointer) + FStackOffset);
  5973. TopOfStack := TopOfStack + FStackOffset;
  5974. end;
  5975. {$ENDIF CPU32}
  5976. // Validate that the code address is a valid code site
  5977. //
  5978. // Information from Intel Manual 24319102(2).pdf, Download the 6.5 MBs from:
  5979. // http://developer.intel.com/design/pentiumii/manuals/243191.htm
  5980. // Instruction format, Chapter 2 and The CALL instruction: page 3-53, 3-54
  5981. function TJclStackInfoList.ValidCallSite(CodeAddr: TJclAddr; out CallInstructionSize: Cardinal): Boolean;
  5982. var
  5983. CodeDWORD4: DWORD;
  5984. CodeDWORD8: DWORD;
  5985. C4P, C8P: PDWORD;
  5986. RM1, RM2, RM5: Byte;
  5987. begin
  5988. // todo: 64 bit version
  5989. // First check that the address is within range of our code segment!
  5990. Result := CodeAddr > 8;
  5991. if Result then
  5992. begin
  5993. C8P := PDWORD(CodeAddr - 8);
  5994. C4P := PDWORD(CodeAddr - 4);
  5995. Result := ValidCodeAddr(TJclAddr(C8P), FModuleInfoList) and not IsBadReadPtr(C8P, 8);
  5996. // Now check to see if the instruction preceding the return address
  5997. // could be a valid CALL instruction
  5998. if Result then
  5999. begin
  6000. try
  6001. CodeDWORD8 := PDWORD(C8P)^;
  6002. CodeDWORD4 := PDWORD(C4P)^;
  6003. // CodeDWORD8 = (ReturnAddr-5):(ReturnAddr-6):(ReturnAddr-7):(ReturnAddr-8)
  6004. // CodeDWORD4 = (ReturnAddr-1):(ReturnAddr-2):(ReturnAddr-3):(ReturnAddr-4)
  6005. // ModR/M bytes contain the following bits:
  6006. // Mod = (76)
  6007. // Reg/Opcode = (543)
  6008. // R/M = (210)
  6009. RM1 := (CodeDWORD4 shr 24) and $7;
  6010. RM2 := (CodeDWORD4 shr 16) and $7;
  6011. //RM3 := (CodeDWORD4 shr 8) and $7;
  6012. //RM4 := CodeDWORD4 and $7;
  6013. RM5 := (CodeDWORD8 shr 24) and $7;
  6014. //RM6 := (CodeDWORD8 shr 16) and $7;
  6015. //RM7 := (CodeDWORD8 shr 8) and $7;
  6016. // Check the instruction prior to the potential call site.
  6017. // We consider it a valid call site if we find a CALL instruction there
  6018. // Check the most common CALL variants first
  6019. if ((CodeDWORD8 and $FF000000) = $E8000000) then
  6020. // 5 bytes, "CALL NEAR REL32" (E8 cd)
  6021. CallInstructionSize := 5
  6022. else
  6023. if ((CodeDWORD4 and $F8FF0000) = $10FF0000) and not (RM1 in [4, 5]) then
  6024. // 2 bytes, "CALL NEAR [EAX]" (FF /2) where Reg = 010, Mod = 00, R/M <> 100 (1 extra byte)
  6025. // and R/M <> 101 (4 extra bytes)
  6026. CallInstructionSize := 2
  6027. else
  6028. if ((CodeDWORD4 and $F8FF0000) = $D0FF0000) then
  6029. // 2 bytes, "CALL NEAR EAX" (FF /2) where Reg = 010 and Mod = 11
  6030. CallInstructionSize := 2
  6031. else
  6032. if ((CodeDWORD4 and $00FFFF00) = $0014FF00) then
  6033. // 3 bytes, "CALL NEAR [EAX+EAX*i]" (FF /2) where Reg = 010, Mod = 00 and RM = 100
  6034. // SIB byte not validated
  6035. CallInstructionSize := 3
  6036. else
  6037. if ((CodeDWORD4 and $00F8FF00) = $0050FF00) and (RM2 <> 4) then
  6038. // 3 bytes, "CALL NEAR [EAX+$12]" (FF /2) where Reg = 010, Mod = 01 and RM <> 100 (1 extra byte)
  6039. CallInstructionSize := 3
  6040. else
  6041. if ((CodeDWORD4 and $0000FFFF) = $000054FF) then
  6042. // 4 bytes, "CALL NEAR [EAX+EAX+$12]" (FF /2) where Reg = 010, Mod = 01 and RM = 100
  6043. // SIB byte not validated
  6044. CallInstructionSize := 4
  6045. else
  6046. if ((CodeDWORD8 and $FFFF0000) = $15FF0000) then
  6047. // 6 bytes, "CALL NEAR [$12345678]" (FF /2) where Reg = 010, Mod = 00 and RM = 101
  6048. CallInstructionSize := 6
  6049. else
  6050. if ((CodeDWORD8 and $F8FF0000) = $90FF0000) and (RM5 <> 4) then
  6051. // 6 bytes, "CALL NEAR [EAX+$12345678]" (FF /2) where Reg = 010, Mod = 10 and RM <> 100 (1 extra byte)
  6052. CallInstructionSize := 6
  6053. else
  6054. if ((CodeDWORD8 and $00FFFF00) = $0094FF00) then
  6055. // 7 bytes, "CALL NEAR [EAX+EAX+$1234567]" (FF /2) where Reg = 010, Mod = 10 and RM = 100
  6056. CallInstructionSize := 7
  6057. else
  6058. if ((CodeDWORD8 and $0000FF00) = $00009A00) then
  6059. // 7 bytes, "CALL FAR $1234:12345678" (9A ptr16:32)
  6060. CallInstructionSize := 7
  6061. else
  6062. Result := False;
  6063. // Because we're not doing a complete disassembly, we will potentially report
  6064. // false positives. If there is odd code that uses the CALL 16:32 format, we
  6065. // can also get false negatives.
  6066. except
  6067. Result := False;
  6068. end;
  6069. end;
  6070. end;
  6071. end;
  6072. {$IFNDEF STACKFRAMES_ON}
  6073. {$STACKFRAMES OFF}
  6074. {$ENDIF ~STACKFRAMES_ON}
  6075. function TJclStackInfoList.ValidStackAddr(StackAddr: TJclAddr): Boolean;
  6076. begin
  6077. Result := (BaseOfStack < StackAddr) and (StackAddr < TopOfStack);
  6078. end;
  6079. //=== Exception frame info routines ==========================================
  6080. function JclCreateExceptFrameList(AIgnoreLevels: Integer): TJclExceptFrameList;
  6081. begin
  6082. Result := TJclExceptFrameList.Create(AIgnoreLevels);
  6083. GlobalStackList.AddObject(Result);
  6084. end;
  6085. function JclLastExceptFrameList: TJclExceptFrameList;
  6086. begin
  6087. Result := GlobalStackList.LastExceptFrameList[GetCurrentThreadID];
  6088. end;
  6089. function JclGetExceptFrameList(ThreadID: DWORD): TJclExceptFrameList;
  6090. begin
  6091. Result := GlobalStackList.LastExceptFrameList[ThreadID];
  6092. end;
  6093. procedure DoExceptFrameTrace;
  6094. begin
  6095. // Ignore first 2 levels; the First level is an undefined frame (I haven't a
  6096. // clue as to where it comes from. The second level is the try..finally block
  6097. // in DoExceptNotify.
  6098. JclCreateExceptFrameList(4);
  6099. end;
  6100. {$OVERFLOWCHECKS OFF}
  6101. function GetJmpDest(Jmp: PJmpInstruction): Pointer;
  6102. begin
  6103. // TODO : 64 bit version
  6104. if Jmp^.opCode = $E9 then
  6105. Result := Pointer(TJclAddr(Jmp) + TJclAddr(Jmp^.distance) + 5)
  6106. else
  6107. if Jmp.opCode = $EB then
  6108. Result := Pointer(TJclAddr(Jmp) + TJclAddr(ShortInt(Jmp^.distance)) + 2)
  6109. else
  6110. Result := nil;
  6111. if (Result <> nil) and (PJmpTable(Result).OPCode = $25FF) then
  6112. if not IsBadReadPtr(PJmpTable(Result).Ptr, SizeOf(Pointer)) then
  6113. Result := Pointer(PJclAddr(PJmpTable(Result).Ptr)^);
  6114. end;
  6115. {$IFDEF OVERFLOWCHECKS_ON}
  6116. {$OVERFLOWCHECKS ON}
  6117. {$ENDIF OVERFLOWCHECKS_ON}
  6118. //=== { TJclExceptFrame } ====================================================
  6119. constructor TJclExceptFrame.Create(AFrameLocation: Pointer; AExcDesc: PExcDesc);
  6120. begin
  6121. inherited Create;
  6122. FFrameKind := efkUnknown;
  6123. FFrameLocation := AFrameLocation;
  6124. FCodeLocation := nil;
  6125. AnalyseExceptFrame(AExcDesc);
  6126. end;
  6127. {$RANGECHECKS OFF}
  6128. procedure TJclExceptFrame.AnalyseExceptFrame(AExcDesc: PExcDesc);
  6129. var
  6130. Dest: Pointer;
  6131. LocInfo: TJclLocationInfo;
  6132. FixedProcedureName: string;
  6133. DotPos, I: Integer;
  6134. begin
  6135. Dest := GetJmpDest(@AExcDesc^.Jmp);
  6136. if Dest <> nil then
  6137. begin
  6138. // get frame kind
  6139. LocInfo := GetLocationInfo(Dest);
  6140. if CompareText(LocInfo.UnitName, 'system') = 0 then
  6141. begin
  6142. FixedProcedureName := LocInfo.ProcedureName;
  6143. DotPos := Pos('.', FixedProcedureName);
  6144. if DotPos > 0 then
  6145. FixedProcedureName := Copy(FixedProcedureName, DotPos + 1, Length(FixedProcedureName) - DotPos);
  6146. if CompareText(FixedProcedureName, '@HandleAnyException') = 0 then
  6147. FFrameKind := efkAnyException
  6148. else
  6149. if CompareText(FixedProcedureName, '@HandleOnException') = 0 then
  6150. FFrameKind := efkOnException
  6151. else
  6152. if CompareText(FixedProcedureName, '@HandleAutoException') = 0 then
  6153. FFrameKind := efkAutoException
  6154. else
  6155. if CompareText(FixedProcedureName, '@HandleFinally') = 0 then
  6156. FFrameKind := efkFinally;
  6157. end;
  6158. // get location
  6159. if FFrameKind <> efkUnknown then
  6160. begin
  6161. FCodeLocation := GetJmpDest(PJmpInstruction(TJclAddr(@AExcDesc^.Instructions)));
  6162. if FCodeLocation = nil then
  6163. FCodeLocation := @AExcDesc^.Instructions;
  6164. end
  6165. else
  6166. begin
  6167. FCodeLocation := GetJmpDest(PJmpInstruction(TJclAddr(AExcDesc)));
  6168. if FCodeLocation = nil then
  6169. FCodeLocation := AExcDesc;
  6170. end;
  6171. // get on handlers
  6172. if FFrameKind = efkOnException then
  6173. begin
  6174. SetLength(FExcTab, AExcDesc^.Cnt);
  6175. for I := 0 to AExcDesc^.Cnt - 1 do
  6176. begin
  6177. if AExcDesc^.ExcTab[I].VTable = nil then
  6178. begin
  6179. SetLength(FExcTab, I);
  6180. Break;
  6181. end
  6182. else
  6183. FExcTab[I] := AExcDesc^.ExcTab[I];
  6184. end;
  6185. end;
  6186. end;
  6187. end;
  6188. {$IFDEF RANGECHECKS_ON}
  6189. {$RANGECHECKS ON}
  6190. {$ENDIF RANGECHECKS_ON}
  6191. function TJclExceptFrame.Handles(ExceptObj: TObject): Boolean;
  6192. var
  6193. Handler: Pointer;
  6194. begin
  6195. Result := HandlerInfo(ExceptObj, Handler);
  6196. end;
  6197. {$OVERFLOWCHECKS OFF}
  6198. function TJclExceptFrame.HandlerInfo(ExceptObj: TObject; out HandlerAt: Pointer): Boolean;
  6199. var
  6200. I: Integer;
  6201. ObjVTable, VTable, ParentVTable: Pointer;
  6202. begin
  6203. Result := FrameKind in [efkAnyException, efkAutoException];
  6204. if not Result and (FrameKind = efkOnException) then
  6205. begin
  6206. HandlerAt := nil;
  6207. ObjVTable := Pointer(ExceptObj.ClassType);
  6208. for I := Low(FExcTab) to High(FExcTab) do
  6209. begin
  6210. VTable := ObjVTable;
  6211. Result := FExcTab[I].VTable = nil;
  6212. while (not Result) and (VTable <> nil) do
  6213. begin
  6214. Result := (FExcTab[I].VTable = VTable) or
  6215. (PShortString(PPointer(PJclAddr(FExcTab[I].VTable)^ + TJclAddr(vmtClassName))^)^ =
  6216. PShortString(PPointer(TJclAddr(VTable) + TJclAddr(vmtClassName))^)^);
  6217. if Result then
  6218. HandlerAt := FExcTab[I].Handler
  6219. else
  6220. begin
  6221. ParentVTable := TClass(VTable).ClassParent;
  6222. if ParentVTable = VTable then
  6223. VTable := nil
  6224. else
  6225. VTable := ParentVTable;
  6226. end;
  6227. end;
  6228. if Result then
  6229. Break;
  6230. end;
  6231. end
  6232. else
  6233. if Result then
  6234. HandlerAt := FCodeLocation
  6235. else
  6236. HandlerAt := nil;
  6237. end;
  6238. {$IFDEF OVERFLOWCHECKS_ON}
  6239. {$OVERFLOWCHECKS ON}
  6240. {$ENDIF OVERFLOWCHECKS_ON}
  6241. //=== { TJclExceptFrameList } ================================================
  6242. constructor TJclExceptFrameList.Create(AIgnoreLevels: Integer);
  6243. begin
  6244. inherited Create;
  6245. FIgnoreLevels := AIgnoreLevels;
  6246. TraceExceptionFrames;
  6247. end;
  6248. function TJclExceptFrameList.AddFrame(AFrame: PExcFrame): TJclExceptFrame;
  6249. begin
  6250. Result := TJclExceptFrame.Create(AFrame, AFrame^.Desc);
  6251. Add(Result);
  6252. end;
  6253. function TJclExceptFrameList.GetItems(Index: Integer): TJclExceptFrame;
  6254. begin
  6255. Result := TJclExceptFrame(Get(Index));
  6256. end;
  6257. procedure TJclExceptFrameList.TraceExceptionFrames;
  6258. {$IFDEF CPU32}
  6259. var
  6260. ExceptionPointer: PExcFrame;
  6261. Level: Integer;
  6262. ModulesList: TJclModuleInfoList;
  6263. begin
  6264. Clear;
  6265. ModulesList := GlobalModulesList.CreateModulesList;
  6266. try
  6267. Level := 0;
  6268. ExceptionPointer := GetExceptionPointer;
  6269. while TJclAddr(ExceptionPointer) <> High(TJclAddr) do
  6270. begin
  6271. if (Level >= IgnoreLevels) and ValidCodeAddr(TJclAddr(ExceptionPointer^.Desc), ModulesList) then
  6272. AddFrame(ExceptionPointer);
  6273. Inc(Level);
  6274. ExceptionPointer := ExceptionPointer^.next;
  6275. end;
  6276. finally
  6277. GlobalModulesList.FreeModulesList(ModulesList);
  6278. end;
  6279. end;
  6280. {$ENDIF CPU32}
  6281. {$IFDEF CPU64}
  6282. begin
  6283. // TODO: 64-bit version
  6284. end;
  6285. {$ENDIF CPU64}
  6286. //=== Exception hooking ======================================================
  6287. var
  6288. TrackingActiveCount: Integer;
  6289. IgnoredExceptions: TThreadList = nil;
  6290. IgnoredExceptionClassNames: TStringList = nil;
  6291. IgnoredExceptionClassNamesCritSect: TJclCriticalSection = nil;
  6292. procedure AddIgnoredException(const ExceptionClass: TClass);
  6293. begin
  6294. if Assigned(ExceptionClass) then
  6295. begin
  6296. if not Assigned(IgnoredExceptions) then
  6297. IgnoredExceptions := TThreadList.Create;
  6298. IgnoredExceptions.Add(ExceptionClass);
  6299. end;
  6300. end;
  6301. procedure AddIgnoredExceptionByName(const AExceptionClassName: string);
  6302. begin
  6303. if AExceptionClassName <> '' then
  6304. begin
  6305. if not Assigned(IgnoredExceptionClassNamesCritSect) then
  6306. IgnoredExceptionClassNamesCritSect := TJclCriticalSection.Create;
  6307. if not Assigned(IgnoredExceptionClassNames) then
  6308. begin
  6309. IgnoredExceptionClassNames := TStringList.Create;
  6310. IgnoredExceptionClassNames.Duplicates := dupIgnore;
  6311. IgnoredExceptionClassNames.Sorted := True;
  6312. end;
  6313. IgnoredExceptionClassNamesCritSect.Enter;
  6314. try
  6315. IgnoredExceptionClassNames.Add(AExceptionClassName);
  6316. finally
  6317. IgnoredExceptionClassNamesCritSect.Leave;
  6318. end;
  6319. end;
  6320. end;
  6321. procedure RemoveIgnoredException(const ExceptionClass: TClass);
  6322. var
  6323. ClassList: TList;
  6324. begin
  6325. if Assigned(ExceptionClass) and Assigned(IgnoredExceptions) then
  6326. begin
  6327. ClassList := IgnoredExceptions.LockList;
  6328. try
  6329. ClassList.Remove(ExceptionClass);
  6330. finally
  6331. IgnoredExceptions.UnlockList;
  6332. end;
  6333. end;
  6334. end;
  6335. procedure RemoveIgnoredExceptionByName(const AExceptionClassName: string);
  6336. var
  6337. Index: Integer;
  6338. begin
  6339. if Assigned(IgnoredExceptionClassNames) and (AExceptionClassName <> '') then
  6340. begin
  6341. IgnoredExceptionClassNamesCritSect.Enter;
  6342. try
  6343. Index := IgnoredExceptionClassNames.IndexOf(AExceptionClassName);
  6344. if Index <> -1 then
  6345. IgnoredExceptionClassNames.Delete(Index);
  6346. finally
  6347. IgnoredExceptionClassNamesCritSect.Leave;
  6348. end;
  6349. end;
  6350. end;
  6351. function IsIgnoredException(const ExceptionClass: TClass): Boolean;
  6352. var
  6353. ClassList: TList;
  6354. Index: Integer;
  6355. begin
  6356. Result := False;
  6357. if Assigned(IgnoredExceptions) and not (stTraceAllExceptions in JclStackTrackingOptions) then
  6358. begin
  6359. ClassList := IgnoredExceptions.LockList;
  6360. try
  6361. for Index := 0 to ClassList.Count - 1 do
  6362. if ExceptionClass.InheritsFrom(TClass(ClassList.Items[Index])) then
  6363. begin
  6364. Result := True;
  6365. Break;
  6366. end;
  6367. finally
  6368. IgnoredExceptions.UnlockList;
  6369. end;
  6370. end;
  6371. if not Result and Assigned(IgnoredExceptionClassNames) and not (stTraceAllExceptions in JclStackTrackingOptions) then
  6372. begin
  6373. IgnoredExceptionClassNamesCritSect.Enter;
  6374. try
  6375. Result := IgnoredExceptionClassNames.IndexOf(ExceptionClass.ClassName) <> -1;
  6376. if not Result then
  6377. for Index := 0 to IgnoredExceptionClassNames.Count - 1 do
  6378. if InheritsFromByName(ExceptionClass, IgnoredExceptionClassNames[Index]) then
  6379. begin
  6380. Result := True;
  6381. Break;
  6382. end;
  6383. finally
  6384. IgnoredExceptionClassNamesCritSect.Leave;
  6385. end;
  6386. end;
  6387. end;
  6388. procedure AddModule(const ModuleName: string);
  6389. begin
  6390. GlobalModulesList.AddModule(ModuleName);
  6391. end;
  6392. procedure DoExceptNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean;
  6393. BaseOfStack: Pointer);
  6394. begin
  6395. if (TrackingActiveCount > 0) and (not (stDisableIfDebuggerAttached in JclStackTrackingOptions) or (not IsDebuggerAttached)) and
  6396. Assigned(ExceptObj) and (not IsIgnoredException(ExceptObj.ClassType)) and
  6397. (not (stMainThreadOnly in JclStackTrackingOptions) or (GetCurrentThreadId = MainThreadID)) then
  6398. begin
  6399. if stStack in JclStackTrackingOptions then
  6400. DoExceptionStackTrace(ExceptObj, ExceptAddr, OSException, BaseOfStack);
  6401. if stExceptFrame in JclStackTrackingOptions then
  6402. DoExceptFrameTrace;
  6403. end;
  6404. end;
  6405. function JclStartExceptionTracking: Boolean;
  6406. begin
  6407. {Increment the tracking count only if exceptions are already being tracked or tracking can be started
  6408. successfully.}
  6409. if TrackingActiveCount = 0 then
  6410. begin
  6411. if JclHookExceptions and JclAddExceptNotifier(DoExceptNotify, npFirstChain) then
  6412. begin
  6413. TrackingActiveCount := 1;
  6414. Result := True;
  6415. end
  6416. else
  6417. Result := False;
  6418. end
  6419. else
  6420. begin
  6421. Inc(TrackingActiveCount);
  6422. Result := False;
  6423. end;
  6424. end;
  6425. function JclStopExceptionTracking: Boolean;
  6426. begin
  6427. {If the current tracking count is 1, an attempt is made to stop tracking exceptions. If successful the
  6428. tracking count is set back to 0. If the current tracking count is > 1 it is simply decremented.}
  6429. if TrackingActiveCount = 1 then
  6430. begin
  6431. Result := JclRemoveExceptNotifier(DoExceptNotify) and JclUnhookExceptions;
  6432. if Result then
  6433. Dec(TrackingActiveCount);
  6434. end
  6435. else
  6436. begin
  6437. if TrackingActiveCount > 0 then
  6438. Dec(TrackingActiveCount);
  6439. Result := False;
  6440. end;
  6441. end;
  6442. function JclExceptionTrackingActive: Boolean;
  6443. begin
  6444. Result := TrackingActiveCount > 0;
  6445. end;
  6446. function JclTrackExceptionsFromLibraries: Boolean;
  6447. begin
  6448. Result := TrackingActiveCount > 0;
  6449. if Result then
  6450. JclInitializeLibrariesHookExcept;
  6451. end;
  6452. //=== Thread exception tracking support ======================================
  6453. var
  6454. RegisteredThreadList: TJclDebugThreadList;
  6455. function JclDebugThreadList: TJclDebugThreadList;
  6456. begin
  6457. if RegisteredThreadList = nil then
  6458. RegisteredThreadList := TJclDebugThreadList.Create;
  6459. Result := RegisteredThreadList;
  6460. end;
  6461. type
  6462. TKernel32_CreateThread = function(SecurityAttributes: Pointer; StackSize: LongWord;
  6463. ThreadFunc: TThreadFunc; Parameter: Pointer;
  6464. CreationFlags: LongWord; var ThreadId: LongWord): Integer; stdcall;
  6465. TKernel32_ExitThread = procedure(ExitCode: Integer); stdcall;
  6466. var
  6467. ThreadsHooked: Boolean;
  6468. Kernel32_CreateThread: TKernel32_CreateThread = nil;
  6469. Kernel32_ExitThread: TKernel32_ExitThread = nil;
  6470. function HookedCreateThread(SecurityAttributes: Pointer; StackSize: LongWord;
  6471. ThreadFunc: TThreadFunc; Parameter: Pointer;
  6472. CreationFlags: LongWord; ThreadId: PLongWord): Integer; stdcall;
  6473. var
  6474. LocalThreadId: LongWord;
  6475. begin
  6476. Result := Kernel32_CreateThread(SecurityAttributes, StackSize, ThreadFunc, Parameter, CreationFlags, LocalThreadId);
  6477. if Result <> 0 then
  6478. begin
  6479. JclDebugThreadList.RegisterThreadID(LocalThreadId);
  6480. if ThreadId <> nil then
  6481. begin
  6482. ThreadId^ := LocalThreadId;
  6483. end;
  6484. end;
  6485. end;
  6486. procedure HookedExitThread(ExitCode: Integer); stdcall;
  6487. begin
  6488. JclDebugThreadList.UnregisterThreadID(GetCurrentThreadID);
  6489. Kernel32_ExitThread(ExitCode);
  6490. end;
  6491. function JclHookThreads: Boolean;
  6492. var
  6493. ProcAddrCache: Pointer;
  6494. begin
  6495. if not ThreadsHooked then
  6496. begin
  6497. ProcAddrCache := GetProcAddress(GetModuleHandle(kernel32), 'CreateThread');
  6498. with TJclPeMapImgHooks do
  6499. Result := ReplaceImport(SystemBase, kernel32, ProcAddrCache, @HookedCreateThread);
  6500. if Result then
  6501. begin
  6502. @Kernel32_CreateThread := ProcAddrCache;
  6503. ProcAddrCache := GetProcAddress(GetModuleHandle(kernel32), 'ExitThread');
  6504. with TJclPeMapImgHooks do
  6505. Result := ReplaceImport(SystemBase, kernel32, ProcAddrCache, @HookedExitThread);
  6506. if Result then
  6507. @Kernel32_ExitThread := ProcAddrCache
  6508. else
  6509. with TJclPeMapImgHooks do
  6510. ReplaceImport(SystemBase, kernel32, @HookedCreateThread, @Kernel32_CreateThread);
  6511. end;
  6512. ThreadsHooked := Result;
  6513. end
  6514. else
  6515. Result := True;
  6516. end;
  6517. function JclUnhookThreads: Boolean;
  6518. begin
  6519. if ThreadsHooked then
  6520. begin
  6521. with TJclPeMapImgHooks do
  6522. begin
  6523. ReplaceImport(SystemBase, kernel32, @HookedCreateThread, @Kernel32_CreateThread);
  6524. ReplaceImport(SystemBase, kernel32, @HookedExitThread, @Kernel32_ExitThread);
  6525. end;
  6526. Result := True;
  6527. ThreadsHooked := False;
  6528. end
  6529. else
  6530. Result := True;
  6531. end;
  6532. function JclThreadsHooked: Boolean;
  6533. begin
  6534. Result := ThreadsHooked;
  6535. end;
  6536. //=== { TJclDebugThread } ====================================================
  6537. constructor TJclDebugThread.Create(ASuspended: Boolean; const AThreadName: string);
  6538. begin
  6539. FThreadName := AThreadName;
  6540. inherited Create(True);
  6541. JclDebugThreadList.RegisterThread(Self, AThreadName);
  6542. if not ASuspended then
  6543. {$IFDEF RTL210_UP}
  6544. Suspended := False;
  6545. {$ELSE ~RTL210_UP}
  6546. Resume;
  6547. {$ENDIF ~RTL210_UP}
  6548. end;
  6549. destructor TJclDebugThread.Destroy;
  6550. begin
  6551. JclDebugThreadList.UnregisterThread(Self);
  6552. inherited Destroy;
  6553. end;
  6554. procedure TJclDebugThread.DoHandleException;
  6555. begin
  6556. GlobalStackList.LockThreadID(ThreadID);
  6557. try
  6558. DoSyncHandleException;
  6559. finally
  6560. GlobalStackList.UnlockThreadID;
  6561. end;
  6562. end;
  6563. procedure TJclDebugThread.DoNotify;
  6564. begin
  6565. JclDebugThreadList.DoSyncException(Self);
  6566. end;
  6567. procedure TJclDebugThread.DoSyncHandleException;
  6568. begin
  6569. // Note: JclLastExceptStackList and JclLastExceptFrameList returns information
  6570. // for this Thread ID instead of MainThread ID here to allow use a common
  6571. // exception handling routine easily.
  6572. // Any other call of those JclLastXXX routines from another thread at the same
  6573. // time will return expected information for current Thread ID.
  6574. DoNotify;
  6575. end;
  6576. function TJclDebugThread.GetThreadInfo: string;
  6577. begin
  6578. Result := JclDebugThreadList.ThreadInfos[ThreadID];
  6579. end;
  6580. procedure TJclDebugThread.HandleException(Sender: TObject);
  6581. begin
  6582. FSyncException := Sender;
  6583. try
  6584. if not Assigned(FSyncException) then
  6585. FSyncException := Exception(ExceptObject);
  6586. if Assigned(FSyncException) and not IsIgnoredException(FSyncException.ClassType) then
  6587. Synchronize(DoHandleException);
  6588. finally
  6589. FSyncException := nil;
  6590. end;
  6591. end;
  6592. //=== { TJclDebugThreadList } ================================================
  6593. type
  6594. TThreadAccess = class(TThread);
  6595. constructor TJclDebugThreadList.Create;
  6596. begin
  6597. FLock := TJclCriticalSection.Create;
  6598. FReadLock := TJclCriticalSection.Create;
  6599. FList := TObjectList.Create;
  6600. FSaveCreationStack := False;
  6601. end;
  6602. destructor TJclDebugThreadList.Destroy;
  6603. begin
  6604. FreeAndNil(FList);
  6605. FreeAndNil(FLock);
  6606. FreeAndNil(FReadLock);
  6607. inherited Destroy;
  6608. end;
  6609. function TJclDebugThreadList.AddStackListToLocationInfoList(ThreadID: DWORD; AList: TJclLocationInfoList): Boolean;
  6610. var
  6611. I: Integer;
  6612. List: TJclStackInfoList;
  6613. begin
  6614. Result := False;
  6615. FReadLock.Enter;
  6616. try
  6617. I := IndexOfThreadID(ThreadID);
  6618. if (I <> -1) and Assigned(TJclDebugThreadInfo(FList[I]).StackList) then
  6619. begin
  6620. List := TJclDebugThreadInfo(FList[I]).StackList;
  6621. AList.AddStackInfoList(List);
  6622. Result := True;
  6623. end;
  6624. finally
  6625. FReadLock.Leave;
  6626. end;
  6627. end;
  6628. procedure TJclDebugThreadList.DoSyncException(Thread: TJclDebugThread);
  6629. begin
  6630. if Assigned(FOnSyncException) then
  6631. FOnSyncException(Thread);
  6632. end;
  6633. procedure TJclDebugThreadList.DoSyncThreadRegistered;
  6634. begin
  6635. if Assigned(FOnThreadRegistered) then
  6636. FOnThreadRegistered(FRegSyncThreadID);
  6637. end;
  6638. procedure TJclDebugThreadList.DoSyncThreadUnregistered;
  6639. begin
  6640. if Assigned(FOnThreadUnregistered) then
  6641. FOnThreadUnregistered(FUnregSyncThreadID);
  6642. end;
  6643. procedure TJclDebugThreadList.DoThreadRegistered(Thread: TThread);
  6644. begin
  6645. if Assigned(FOnThreadRegistered) then
  6646. begin
  6647. FRegSyncThreadID := Thread.ThreadID;
  6648. TThreadAccess(Thread).Synchronize(DoSyncThreadRegistered);
  6649. end;
  6650. end;
  6651. procedure TJclDebugThreadList.DoThreadUnregistered(Thread: TThread);
  6652. begin
  6653. if Assigned(FOnThreadUnregistered) then
  6654. begin
  6655. FUnregSyncThreadID := Thread.ThreadID;
  6656. TThreadAccess(Thread).Synchronize(DoSyncThreadUnregistered);
  6657. end;
  6658. end;
  6659. function TJclDebugThreadList.GetThreadClassNames(ThreadID: DWORD): string;
  6660. begin
  6661. Result := GetThreadValues(ThreadID, 1);
  6662. end;
  6663. function TJclDebugThreadList.GetThreadCreationTime(ThreadID: DWORD): TDateTime;
  6664. var
  6665. I: Integer;
  6666. begin
  6667. FReadLock.Enter;
  6668. try
  6669. I := IndexOfThreadID(ThreadID);
  6670. if I <> -1 then
  6671. Result := TJclDebugThreadInfo(FList[I]).CreationTime
  6672. else
  6673. Result := 0;
  6674. finally
  6675. FReadLock.Leave;
  6676. end;
  6677. end;
  6678. function TJclDebugThreadList.GetThreadIDCount: Integer;
  6679. begin
  6680. FReadLock.Enter;
  6681. try
  6682. Result := FList.Count;
  6683. finally
  6684. FReadLock.Leave;
  6685. end;
  6686. end;
  6687. function TJclDebugThreadList.GetThreadHandle(Index: Integer): THandle;
  6688. begin
  6689. FReadLock.Enter;
  6690. try
  6691. Result := TJclDebugThreadInfo(FList[Index]).ThreadHandle;
  6692. finally
  6693. FReadLock.Leave;
  6694. end;
  6695. end;
  6696. function TJclDebugThreadList.GetThreadID(Index: Integer): DWORD;
  6697. begin
  6698. FReadLock.Enter;
  6699. try
  6700. Result := TJclDebugThreadInfo(FList[Index]).ThreadID;
  6701. finally
  6702. FReadLock.Leave;
  6703. end;
  6704. end;
  6705. function TJclDebugThreadList.GetThreadInfos(ThreadID: DWORD): string;
  6706. begin
  6707. Result := GetThreadValues(ThreadID, 2);
  6708. end;
  6709. function TJclDebugThreadList.GetThreadNames(ThreadID: DWORD): string;
  6710. begin
  6711. Result := GetThreadValues(ThreadID, 0);
  6712. end;
  6713. function TJclDebugThreadList.GetThreadParentID(ThreadID: DWORD): DWORD;
  6714. var
  6715. I: Integer;
  6716. begin
  6717. FReadLock.Enter;
  6718. try
  6719. I := IndexOfThreadID(ThreadID);
  6720. if I <> -1 then
  6721. Result := TJclDebugThreadInfo(FList[I]).ParentThreadID
  6722. else
  6723. Result := 0;
  6724. finally
  6725. FReadLock.Leave;
  6726. end;
  6727. end;
  6728. function TJclDebugThreadList.GetThreadValues(ThreadID: DWORD; Index: Integer): string;
  6729. var
  6730. I: Integer;
  6731. begin
  6732. FReadLock.Enter;
  6733. try
  6734. I := IndexOfThreadID(ThreadID);
  6735. if I <> -1 then
  6736. begin
  6737. case Index of
  6738. 0:
  6739. Result := TJclDebugThreadInfo(FList[I]).ThreadName;
  6740. 1:
  6741. Result := TJclDebugThreadInfo(FList[I]).ThreadClassName;
  6742. 2:
  6743. Result := Format('%.8x [%s] "%s"', [ThreadID, TJclDebugThreadInfo(FList[I]).ThreadClassName,
  6744. TJclDebugThreadInfo(FList[I]).ThreadName]);
  6745. end;
  6746. end
  6747. else
  6748. Result := '';
  6749. finally
  6750. FReadLock.Leave;
  6751. end;
  6752. end;
  6753. function TJclDebugThreadList.IndexOfThreadID(ThreadID: DWORD): Integer;
  6754. var
  6755. I: Integer;
  6756. begin
  6757. Result := -1;
  6758. for I := FList.Count - 1 downto 0 do
  6759. if TJclDebugThreadInfo(FList[I]).ThreadID = ThreadID then
  6760. begin
  6761. Result := I;
  6762. Break;
  6763. end;
  6764. end;
  6765. procedure TJclDebugThreadList.InternalRegisterThread(Thread: TThread; ThreadID: DWORD; const ThreadName: string);
  6766. var
  6767. I: Integer;
  6768. ThreadInfo: TJclDebugThreadInfo;
  6769. begin
  6770. FLock.Enter;
  6771. try
  6772. I := IndexOfThreadID(ThreadID);
  6773. if I = -1 then
  6774. begin
  6775. FReadLock.Enter;
  6776. try
  6777. FList.Add(TJclDebugThreadInfo.Create(GetCurrentThreadId, ThreadID, FSaveCreationStack));
  6778. ThreadInfo := TJclDebugThreadInfo(FList.Last);
  6779. if Assigned(Thread) then
  6780. begin
  6781. ThreadInfo.ThreadHandle := Thread.Handle;
  6782. ThreadInfo.ThreadClassName := Thread.ClassName;
  6783. end
  6784. else
  6785. begin
  6786. ThreadInfo.ThreadHandle := 0;
  6787. ThreadInfo.ThreadClassName := '';
  6788. end;
  6789. ThreadInfo.ThreadName := ThreadName;
  6790. finally
  6791. FReadLock.Leave;
  6792. end;
  6793. if Assigned(Thread) then
  6794. DoThreadRegistered(Thread);
  6795. end;
  6796. finally
  6797. FLock.Leave;
  6798. end;
  6799. end;
  6800. procedure TJclDebugThreadList.InternalUnregisterThread(Thread: TThread; ThreadID: DWORD);
  6801. var
  6802. I: Integer;
  6803. begin
  6804. FLock.Enter;
  6805. try
  6806. I := IndexOfThreadID(ThreadID);
  6807. if I <> -1 then
  6808. begin
  6809. if Assigned(Thread) then
  6810. DoThreadUnregistered(Thread);
  6811. FReadLock.Enter;
  6812. try
  6813. FList.Delete(I);
  6814. finally
  6815. FReadLock.Leave;
  6816. end;
  6817. end;
  6818. finally
  6819. FLock.Leave;
  6820. end;
  6821. end;
  6822. procedure TJclDebugThreadList.RegisterThread(Thread: TThread; const ThreadName: string);
  6823. begin
  6824. InternalRegisterThread(Thread, Thread.ThreadID, ThreadName);
  6825. end;
  6826. procedure TJclDebugThreadList.RegisterThreadID(AThreadID: DWORD; const ThreadName: string);
  6827. begin
  6828. InternalRegisterThread(nil, AThreadID, ThreadName);
  6829. end;
  6830. procedure TJclDebugThreadList.UnregisterThread(Thread: TThread);
  6831. begin
  6832. InternalUnregisterThread(Thread, Thread.ThreadID);
  6833. end;
  6834. procedure TJclDebugThreadList.UnregisterThreadID(AThreadID: DWORD);
  6835. begin
  6836. InternalUnregisterThread(nil, AThreadID);
  6837. end;
  6838. //=== { TJclDebugThreadInfo } ================================================
  6839. constructor TJclDebugThreadInfo.Create(AParentThreadID, AThreadID: DWORD; AStack: Boolean);
  6840. begin
  6841. FCreationTime := Now;
  6842. FParentThreadID := AParentThreadID;
  6843. try
  6844. { TODO -oUSc : ... }
  6845. // FStackList := JclCreateStackList(True, 0, nil, True);//probably IgnoreLevels = 11
  6846. if AStack then
  6847. FStackList := TJclStackInfoList.Create(True, 0, nil, True, nil, nil)
  6848. else
  6849. FStackList := nil;
  6850. except
  6851. FStackList := nil;
  6852. end;
  6853. FThreadID := AThreadID;
  6854. end;
  6855. destructor TJclDebugThreadInfo.Destroy;
  6856. begin
  6857. FStackList.Free;
  6858. inherited Destroy;
  6859. end;
  6860. //=== { TJclCustomThreadInfo } ===============================================
  6861. constructor TJclCustomThreadInfo.Create;
  6862. var
  6863. StackClass: TJclCustomLocationInfoListClass;
  6864. begin
  6865. inherited Create;
  6866. StackClass := GetStackClass;
  6867. FCreationTime := 0;
  6868. FCreationStack := StackClass.Create;
  6869. FName := '';
  6870. FParentThreadID := 0;
  6871. FStack := StackClass.Create;
  6872. FThreadID := 0;
  6873. FValues := [];
  6874. end;
  6875. destructor TJclCustomThreadInfo.Destroy;
  6876. begin
  6877. FCreationStack.Free;
  6878. FStack.Free;
  6879. inherited Destroy;
  6880. end;
  6881. procedure TJclCustomThreadInfo.AssignTo(Dest: TPersistent);
  6882. begin
  6883. if Dest is TJclCustomThreadInfo then
  6884. begin
  6885. TJclCustomThreadInfo(Dest).FCreationTime := FCreationTime;
  6886. TJclCustomThreadInfo(Dest).FCreationStack.Assign(FCreationStack);
  6887. TJclCustomThreadInfo(Dest).FName := FName;
  6888. TJclCustomThreadInfo(Dest).FParentThreadID := FParentThreadID;
  6889. TJclCustomThreadInfo(Dest).FStack.Assign(FStack);
  6890. TJclCustomThreadInfo(Dest).FThreadID := FThreadID;
  6891. TJclCustomThreadInfo(Dest).FValues := FValues;
  6892. end
  6893. else
  6894. inherited AssignTo(Dest);
  6895. end;
  6896. function TJclCustomThreadInfo.GetStackClass: TJclCustomLocationInfoListClass;
  6897. begin
  6898. Result := TJclLocationInfoList;
  6899. end;
  6900. //=== { TJclThreadInfo } =====================================================
  6901. procedure TJclThreadInfo.Fill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions);
  6902. begin
  6903. InternalFill(AThreadHandle, AThreadID, AGatherOptions, False);
  6904. end;
  6905. procedure TJclThreadInfo.FillFromExceptThread(AGatherOptions: TJclThreadInfoOptions);
  6906. begin
  6907. InternalFill(0, GetCurrentThreadID, AGatherOptions, True);
  6908. end;
  6909. function TJclThreadInfo.GetAsString: string;
  6910. var
  6911. ExceptInfo, ThreadName, ThreadInfoStr: string;
  6912. begin
  6913. if tioIsMainThread in Values then
  6914. ThreadName := ' [MainThread]'
  6915. else
  6916. if tioName in Values then
  6917. ThreadName := Name
  6918. else
  6919. ThreadName := '';
  6920. ThreadInfoStr := '';
  6921. if tioCreationTime in Values then
  6922. ThreadInfoStr := ThreadInfoStr + Format(' CreationTime: %s', [DateTimeToStr(CreationTime)]);
  6923. if tioParentThreadID in Values then
  6924. ThreadInfoStr := ThreadInfoStr + Format(' ParentThreadID: %d', [ParentThreadID]);
  6925. ExceptInfo := Format('ThreadID: %d%s%s', [ThreadID, ThreadName, ThreadInfoStr]) + #13#10;
  6926. if tioStack in Values then
  6927. ExceptInfo := ExceptInfo + Stack.AsString;
  6928. if tioCreationStack in Values then
  6929. ExceptInfo := ExceptInfo + 'Created at:' + #13#10 + CreationStack.AsString + #13#10;
  6930. Result := ExceptInfo + #13#10;
  6931. end;
  6932. function TJclThreadInfo.GetStack(const AIndex: Integer): TJclLocationInfoList;
  6933. begin
  6934. case AIndex of
  6935. 1: Result := TJclLocationInfoList(FCreationStack);
  6936. 2: Result := TJclLocationInfoList(FStack);
  6937. else
  6938. Result := nil;
  6939. end;
  6940. end;
  6941. function TJclThreadInfo.GetStackClass: TJclCustomLocationInfoListClass;
  6942. begin
  6943. Result := TJclLocationInfoList;
  6944. end;
  6945. procedure TJclThreadInfo.InternalFill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions; AExceptThread: Boolean);
  6946. var
  6947. Idx: Integer;
  6948. List: TJclStackInfoList;
  6949. begin
  6950. if tioStack in AGatherOptions then
  6951. begin
  6952. if AExceptThread then
  6953. List := JclLastExceptStackList
  6954. else
  6955. List := JclCreateThreadStackTrace(True, AThreadHandle);
  6956. try
  6957. Stack.AddStackInfoList(List);
  6958. Values := Values + [tioStack];
  6959. except
  6960. { TODO -oUSc : ... }
  6961. end;
  6962. end;
  6963. ThreadID := AThreadID;
  6964. if tioIsMainThread in AGatherOptions then
  6965. begin
  6966. if MainThreadID = AThreadID then
  6967. Values := Values + [tioIsMainThread];
  6968. end;
  6969. if AGatherOptions * [tioName, tioCreationTime, tioParentThreadID, tioCreationStack] <> [] then
  6970. Idx := JclDebugThreadList.IndexOfThreadID(AThreadID)
  6971. else
  6972. Idx := -1;
  6973. if (tioName in AGatherOptions) and (Idx <> -1) then
  6974. begin
  6975. Name := JclDebugThreadList.ThreadNames[AThreadID];
  6976. Values := Values + [tioName];
  6977. end;
  6978. if (tioCreationTime in AGatherOptions) and (Idx <> -1) then
  6979. begin
  6980. CreationTime := JclDebugThreadList.ThreadCreationTime[AThreadID];
  6981. Values := Values + [tioCreationTime];
  6982. end;
  6983. if (tioParentThreadID in AGatherOptions) and (Idx <> -1) then
  6984. begin
  6985. ParentThreadID := JclDebugThreadList.ThreadParentIDs[AThreadID];
  6986. Values := Values + [tioParentThreadID];
  6987. end;
  6988. if (tioCreationStack in AGatherOptions) and (Idx <> -1) then
  6989. begin
  6990. try
  6991. if JclDebugThreadList.AddStackListToLocationInfoList(AThreadID, CreationStack) then
  6992. Values := Values + [tioCreationStack];
  6993. except
  6994. { TODO -oUSc : ... }
  6995. end;
  6996. end;
  6997. end;
  6998. //=== { TJclThreadInfoList } =================================================
  6999. constructor TJclThreadInfoList.Create;
  7000. begin
  7001. inherited Create;
  7002. FItems := TObjectList.Create;
  7003. FGatherOptions := [tioIsMainThread, tioName, tioCreationTime, tioParentThreadID, tioStack, tioCreationStack];
  7004. end;
  7005. destructor TJclThreadInfoList.Destroy;
  7006. begin
  7007. FItems.Free;
  7008. inherited Destroy;
  7009. end;
  7010. function TJclThreadInfoList.Add: TJclThreadInfo;
  7011. begin
  7012. FItems.Add(TJclThreadInfo.Create);
  7013. Result := TJclThreadInfo(FItems.Last);
  7014. end;
  7015. procedure TJclThreadInfoList.AssignTo(Dest: TPersistent);
  7016. var
  7017. I: Integer;
  7018. begin
  7019. if Dest is TJclThreadInfoList then
  7020. begin
  7021. TJclThreadInfoList(Dest).Clear;
  7022. for I := 0 to Count - 1 do
  7023. TJclThreadInfoList(Dest).Add.Assign(Items[I]);
  7024. TJclThreadInfoList(Dest).GatherOptions := FGatherOptions;
  7025. end
  7026. else
  7027. inherited AssignTo(Dest);
  7028. end;
  7029. procedure TJclThreadInfoList.Clear;
  7030. begin
  7031. FItems.Clear;
  7032. end;
  7033. function TJclThreadInfoList.GetAsString: string;
  7034. var
  7035. I: Integer;
  7036. begin
  7037. Result := '';
  7038. for I := 0 to Count - 1 do
  7039. Result := Result + Items[I].AsString + #13#10;
  7040. end;
  7041. procedure TJclThreadInfoList.Gather(AExceptThreadID: DWORD);
  7042. begin
  7043. InternalGather([], [AExceptThreadID]);
  7044. end;
  7045. procedure TJclThreadInfoList.GatherExclude(AThreadIDs: array of DWORD);
  7046. begin
  7047. InternalGather([], AThreadIDs);
  7048. end;
  7049. procedure TJclThreadInfoList.GatherInclude(AThreadIDs: array of DWORD);
  7050. begin
  7051. InternalGather(AThreadIDs, []);
  7052. end;
  7053. function TJclThreadInfoList.GetCount: Integer;
  7054. begin
  7055. Result := FItems.Count;
  7056. end;
  7057. function TJclThreadInfoList.GetItems(AIndex: Integer): TJclThreadInfo;
  7058. begin
  7059. Result := TJclThreadInfo(FItems[AIndex]);
  7060. end;
  7061. procedure TJclThreadInfoList.InternalGather(AIncludeThreadIDs, AExcludeThreadIDs: array of DWORD);
  7062. function OpenThread(ThreadID: DWORD): THandle;
  7063. type
  7064. TOpenThreadFunc = function(DesiredAccess: DWORD; InheritHandle: BOOL; ThreadID: DWORD): THandle; stdcall;
  7065. const
  7066. THREAD_SUSPEND_RESUME = $0002;
  7067. THREAD_GET_CONTEXT = $0008;
  7068. THREAD_QUERY_INFORMATION = $0040;
  7069. var
  7070. Kernel32Lib: THandle;
  7071. OpenThreadFunc: TOpenThreadFunc;
  7072. begin
  7073. Result := 0;
  7074. Kernel32Lib := GetModuleHandle(kernel32);
  7075. if Kernel32Lib <> 0 then
  7076. begin
  7077. // OpenThread only exists since Windows ME
  7078. OpenThreadFunc := GetProcAddress(Kernel32Lib, 'OpenThread');
  7079. if Assigned(OpenThreadFunc) then
  7080. Result := OpenThreadFunc(THREAD_SUSPEND_RESUME or THREAD_GET_CONTEXT or THREAD_QUERY_INFORMATION, False, ThreadID);
  7081. end;
  7082. end;
  7083. function SearchThreadInArray(AThreadIDs: array of DWORD; AThreadID: DWORD): Boolean;
  7084. var
  7085. I: Integer;
  7086. begin
  7087. Result := False;
  7088. if Length(AThreadIDs) > 0 then
  7089. for I := Low(AThreadIDs) to High(AThreadIDs) do
  7090. if AThreadIDs[I] = AThreadID then
  7091. begin
  7092. Result := True;
  7093. Break;
  7094. end;
  7095. end;
  7096. var
  7097. SnapProcHandle: THandle;
  7098. ThreadEntry: TThreadEntry32;
  7099. NextThread: Boolean;
  7100. ThreadIDList, ThreadHandleList: TList;
  7101. I: Integer;
  7102. PID, TID: DWORD;
  7103. ThreadHandle: THandle;
  7104. ThreadInfo: TJclThreadInfo;
  7105. begin
  7106. ThreadIDList := TList.Create;
  7107. ThreadHandleList := TList.Create;
  7108. try
  7109. SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  7110. if SnapProcHandle <> INVALID_HANDLE_VALUE then
  7111. try
  7112. PID := GetCurrentProcessId;
  7113. ThreadEntry.dwSize := SizeOf(ThreadEntry);
  7114. NextThread := Thread32First(SnapProcHandle, ThreadEntry);
  7115. while NextThread do
  7116. begin
  7117. if ThreadEntry.th32OwnerProcessID = PID then
  7118. begin
  7119. if SearchThreadInArray(AIncludeThreadIDs, ThreadEntry.th32ThreadID) or
  7120. not SearchThreadInArray(AExcludeThreadIDs, ThreadEntry.th32ThreadID) then
  7121. ThreadIDList.Add(Pointer(ThreadEntry.th32ThreadID));
  7122. end;
  7123. NextThread := Thread32Next(SnapProcHandle, ThreadEntry);
  7124. end;
  7125. finally
  7126. CloseHandle(SnapProcHandle);
  7127. end;
  7128. for I := 0 to ThreadIDList.Count - 1 do
  7129. begin
  7130. ThreadHandle := OpenThread(TJclAddr(ThreadIDList[I]));
  7131. ThreadHandleList.Add(Pointer(ThreadHandle));
  7132. if ThreadHandle <> 0 then
  7133. SuspendThread(ThreadHandle);
  7134. end;
  7135. try
  7136. for I := 0 to ThreadIDList.Count - 1 do
  7137. begin
  7138. ThreadHandle := THandle(ThreadHandleList[I]);
  7139. TID := TJclAddr(ThreadIDList[I]);
  7140. ThreadInfo := Add;
  7141. ThreadInfo.Fill(ThreadHandle, TID, FGatherOptions);
  7142. end;
  7143. finally
  7144. for I := 0 to ThreadHandleList.Count - 1 do
  7145. if ThreadHandleList[I] <> nil then
  7146. begin
  7147. ThreadHandle := THandle(ThreadHandleList[I]);
  7148. ResumeThread(ThreadHandle);
  7149. CloseHandle(ThreadHandle);
  7150. end;
  7151. end;
  7152. finally
  7153. ThreadIDList.Free;
  7154. ThreadHandleList.Free;
  7155. end;
  7156. end;
  7157. //== Miscellanuous ===========================================================
  7158. {$IFDEF MSWINDOWS}
  7159. function EnableCrashOnCtrlScroll(const Enable: Boolean): Boolean;
  7160. const
  7161. CrashCtrlScrollKey = 'SYSTEM\CurrentControlSet\Services\i8042prt\Parameters';
  7162. CrashCtrlScrollName = 'CrashOnCtrlScroll';
  7163. var
  7164. Enabled: Integer;
  7165. begin
  7166. Enabled := 0;
  7167. if Enable then
  7168. Enabled := 1;
  7169. RegWriteInteger(HKEY_LOCAL_MACHINE, CrashCtrlScrollKey, CrashCtrlScrollName, Enabled);
  7170. Result := RegReadInteger(HKEY_LOCAL_MACHINE, CrashCtrlScrollKey, CrashCtrlScrollName) = Enabled;
  7171. end;
  7172. function IsDebuggerAttached: Boolean;
  7173. var
  7174. IsDebuggerPresent: function: Boolean; stdcall;
  7175. KernelHandle: THandle;
  7176. P: Pointer;
  7177. begin
  7178. KernelHandle := GetModuleHandle(kernel32);
  7179. @IsDebuggerPresent := GetProcAddress(KernelHandle, 'IsDebuggerPresent');
  7180. if @IsDebuggerPresent <> nil then
  7181. begin
  7182. // Win98+ / NT4+
  7183. Result := IsDebuggerPresent
  7184. end
  7185. else
  7186. begin
  7187. // Win9x uses thunk pointer outside the module when under a debugger
  7188. P := GetProcAddress(KernelHandle, 'GetProcAddress');
  7189. Result := TJclAddr(P) < KernelHandle;
  7190. end;
  7191. end;
  7192. function IsHandleValid(Handle: THandle): Boolean;
  7193. var
  7194. Duplicate: THandle;
  7195. Flags: DWORD;
  7196. begin
  7197. if IsWinNT then
  7198. begin
  7199. Flags := 0;
  7200. Result := GetHandleInformation(Handle, Flags);
  7201. end
  7202. else
  7203. Result := False;
  7204. if not Result then
  7205. begin
  7206. // DuplicateHandle is used as an additional check for those object types not
  7207. // supported by GetHandleInformation (e.g. according to the documentation,
  7208. // GetHandleInformation doesn't support window stations and desktop although
  7209. // tests show that it does). GetHandleInformation is tried first because its
  7210. // much faster. Additionally GetHandleInformation is only supported on NT...
  7211. Result := DuplicateHandle(GetCurrentProcess, Handle, GetCurrentProcess,
  7212. @Duplicate, 0, False, DUPLICATE_SAME_ACCESS);
  7213. if Result then
  7214. Result := CloseHandle(Duplicate);
  7215. end;
  7216. end;
  7217. {$ENDIF MSWINDOWS}
  7218. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  7219. type
  7220. PJclStackInfoRec = ^TJclStackInfoRec;
  7221. TJclStackInfoRec = record
  7222. Stack: TJclStackInfoList;
  7223. Stacktrace: string;
  7224. end;
  7225. procedure ResolveStackInfoRec(Info: PJclStackInfoRec);
  7226. var
  7227. Str: TStringList;
  7228. begin
  7229. if (Info <> nil) and (Info.Stack <> nil) then
  7230. begin
  7231. Str := TStringList.Create;
  7232. try
  7233. Info.Stack.AddToStrings(Str,
  7234. estoIncludeModuleName in JclExceptionStacktraceOptions,
  7235. estoIncludeAdressOffset in JclExceptionStacktraceOptions,
  7236. estoIncludeStartProcLineOffset in JclExceptionStacktraceOptions,
  7237. estoIncludeVAddress in JclExceptionStacktraceOptions
  7238. );
  7239. FreeAndNil(Info.Stack);
  7240. Info.Stacktrace := Str.Text;
  7241. finally
  7242. FreeAndNil(Str);
  7243. end;
  7244. end;
  7245. end;
  7246. procedure CleanUpStackInfo(Info: Pointer);
  7247. begin
  7248. if Info <> nil then
  7249. begin
  7250. PJclStackInfoRec(Info).Stack.Free;
  7251. Dispose(PJclStackInfoRec(Info));
  7252. end;
  7253. end;
  7254. {$STACKFRAMES ON}
  7255. // We use the StackFrame's Base-Pointer to skip all local variables from this function
  7256. function GetExceptionStackInfo(P: PExceptionRecord): Pointer;
  7257. const
  7258. cDelphiException = $0EEDFADE;
  7259. cSetThreadNameException = $406D1388;
  7260. var
  7261. Stack: TJclStackInfoList;
  7262. Info: PJclStackInfoRec;
  7263. RawMode: Boolean;
  7264. Delayed: Boolean;
  7265. IgnoreLevels: Integer;
  7266. begin
  7267. if P^.ExceptionCode = cSetThreadNameException then
  7268. begin
  7269. Result := nil;
  7270. Exit;
  7271. end;
  7272. RawMode := stRawMode in JclStackTrackingOptions;
  7273. Delayed := stDelayedTrace in JclStackTrackingOptions;
  7274. IgnoreLevels := 0;
  7275. if RawMode then
  7276. begin
  7277. // Skip RaiseExceptionObject, System.@RaiseExcept and the function causing the exception.
  7278. // The causing function is added again as the first stack item through P.ExceptionAddress.
  7279. if (P.ExceptionAddress <> nil) and (P^.ExceptionCode = cDelphiException) then
  7280. Inc(IgnoreLevels, 3)
  7281. else
  7282. Inc(IgnoreLevels, 2);
  7283. end;
  7284. if P^.ExceptionCode = cDelphiException then
  7285. begin
  7286. if (P^.ExceptObject <> nil) and (Exception(P.ExceptObject).StackInfo <> nil) then
  7287. begin
  7288. // This method is called twice for the same exception object if the user calls
  7289. // AcquireExceptionObject and then throws this exception again. In this case the
  7290. // StackInfo is already allocated and by overwriting it we produce a memory leak.
  7291. // Example: "E := AcquireExceptionObject; raise E;"
  7292. Result := Exception(P.ExceptObject).StackInfo;
  7293. Exit;
  7294. end;
  7295. if (P^.ExceptObject <> nil) and
  7296. not (stTraceAllExceptions in JclStackTrackingOptions) and
  7297. IsIgnoredException(TObject(P^.ExceptObject).ClassType) then
  7298. begin
  7299. Result := nil;
  7300. Exit;
  7301. end;
  7302. Stack := TJclStackInfoList.Create(RawMode, IgnoreLevels, P^.ExceptAddr, Delayed, GetFramePointer); // Don't add it to the GlobalStackList
  7303. end
  7304. else
  7305. Stack := TJclStackInfoList.Create(RawMode, IgnoreLevels, P^.ExceptionAddress, Delayed, GetFramePointer); // Don't add it to the GlobalStackList
  7306. New(Info);
  7307. Info.Stack := Stack;
  7308. if stImmediateExceptionStacktraceResolving in JclStackTrackingOptions then
  7309. begin
  7310. try
  7311. ResolveStackInfoRec(Info);
  7312. except
  7313. CleanUpStackInfo(Info);
  7314. Info := nil;
  7315. end;
  7316. end;
  7317. Result := Info;
  7318. end;
  7319. {$IFDEF STACKFRAMES_ON}
  7320. {$STACKFRAMES ON}
  7321. {$ENDIF STACKFRAMES_ON}
  7322. function GetStackInfoString(Info: Pointer): string;
  7323. var
  7324. Rec: PJclStackInfoRec;
  7325. begin
  7326. Rec := Info;
  7327. if Rec <> nil then
  7328. begin
  7329. if Rec.Stack <> nil then
  7330. ResolveStackInfoRec(Rec);
  7331. Result := Rec.Stacktrace;
  7332. end
  7333. else
  7334. Result := '';
  7335. end;
  7336. procedure SetupExceptionProcs;
  7337. begin
  7338. if not Assigned(Exception.GetExceptionStackInfoProc) then
  7339. begin
  7340. Exception.GetExceptionStackInfoProc := GetExceptionStackInfo;
  7341. Exception.GetStackInfoStringProc := GetStackInfoString;
  7342. Exception.CleanUpStackInfoProc := CleanUpStackInfo;
  7343. end;
  7344. end;
  7345. procedure ResetExceptionProcs;
  7346. begin
  7347. if @Exception.GetExceptionStackInfoProc = @GetExceptionStackInfo then
  7348. begin
  7349. Exception.GetExceptionStackInfoProc := nil;
  7350. Exception.GetStackInfoStringProc := nil;
  7351. Exception.CleanUpStackInfoProc := nil;
  7352. end;
  7353. end;
  7354. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  7355. procedure InitHexMap;
  7356. var
  7357. Ch: AnsiChar;
  7358. begin
  7359. FillChar(HexMap, SizeOf(HexMap), $80);
  7360. for Ch := '0' to '9' do
  7361. HexMap[Ch] := Ord(Ch) - Ord('0');
  7362. for Ch := 'a' to 'f' do
  7363. HexMap[Ch] := Ord(Ch) - (Ord('a') - 10);
  7364. for Ch := 'A' to 'F' do
  7365. HexMap[Ch] := Ord(Ch) - (Ord('A') - 10);
  7366. end;
  7367. procedure FreeJclDebugGlobals;
  7368. begin
  7369. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  7370. ResetExceptionProcs;
  7371. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  7372. FreeAndNil(RegisteredThreadList);
  7373. FreeAndNil(DebugInfoList);
  7374. FreeAndNil(GlobalStackList);
  7375. FreeAndNil(GlobalModulesList);
  7376. FreeAndNil(DebugInfoCritSect);
  7377. FreeAndNil(InfoSourceClassList);
  7378. FreeAndNil(IgnoredExceptions);
  7379. FreeAndNil(IgnoredExceptionClassNames);
  7380. FreeAndNil(IgnoredExceptionClassNamesCritSect);
  7381. TJclDebugInfoSymbols.CleanupDebugSymbols;
  7382. end;
  7383. initialization
  7384. InitHexMap;
  7385. DebugInfoCritSect := TJclCriticalSection.Create;
  7386. GlobalModulesList := TJclGlobalModulesList.Create;
  7387. GlobalStackList := TJclGlobalStackList.Create;
  7388. AddIgnoredException(EAbort);
  7389. {$IFDEF UNITVERSIONING}
  7390. RegisterUnitVersion(HInstance, UnitVersioning);
  7391. {$ENDIF UNITVERSIONING}
  7392. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  7393. SetupExceptionProcs;
  7394. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  7395. finalization
  7396. {$IFDEF UNITVERSIONING}
  7397. UnregisterUnitVersion(HInstance);
  7398. {$ENDIF UNITVERSIONING}
  7399. { TODO -oPV -cInvestigate : Calling JclStopExceptionTracking causes linking of various classes to
  7400. the code without a real need. Although there doesn't seem to be a way to unhook exceptions
  7401. safely because we need to be covered by JclHookExcept.Notifiers critical section }
  7402. JclStopExceptionTracking;
  7403. GlobalStackList.Clear;
  7404. JclDebugFinalized := True;
  7405. if GlobalStackListLiveCount = 0 then
  7406. FreeJclDebugGlobals;
  7407. end.