JclDebug.pas 220 KB

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