JclDebug.pas 259 KB

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