JclDebug.pas 221 KB

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