1
0

JclDebug.pas 218 KB

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