JclDebug.pas 261 KB

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