| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177 | {**************************************************************************************************}{                                                                                                  }{ Project JEDI Code Library (JCL)                                                                  }{                                                                                                  }{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }{ you may not use this file except in compliance with the License. You may obtain a copy of the    }{ License at http://www.mozilla.org/MPL/                                                           }{                                                                                                  }{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }{ ANY KIND, either express or implied. See the License for the specific language governing rights  }{ and limitations under the License.                                                               }{                                                                                                  }{ The Original Code is JclDebug.pas.                                                               }{                                                                                                  }{ The Initial Developers of the Original Code are Petr Vones and Marcel van Brakel.                }{ Portions created by these individuals are Copyright (C) of these individuals.                    }{ All Rights Reserved.                                                                             }{                                                                                                  }{ Contributor(s):                                                                                  }{   Marcel van Brakel                                                                              }{   Flier Lu (flier)                                                                               }{   Florent Ouchet (outchy)                                                                        }{   Robert Marquardt (marquardt)                                                                   }{   Robert Rossmair (rrossmair)                                                                    }{   Andreas Hausladen (ahuser)                                                                     }{   Petr Vones (pvones)                                                                            }{   Soeren Muehlbauer                                                                              }{   Uwe Schuster (uschuster)                                                                       }{                                                                                                  }{**************************************************************************************************}{                                                                                                  }{ Various debugging support routines and classes. This includes: Diagnostics routines, Trace       }{ routines, Stack tracing and Source Locations a la the C/C++ __FILE__ and __LINE__ macros.        }{                                                                                                  }{**************************************************************************************************}{                                                                                                  }{ Last modified: $Date::                                                                         $ }{ Revision:      $Rev::                                                                          $ }{ Author:        $Author::                                                                       $ }{                                                                                                  }{**************************************************************************************************}unit JclDebug;interface{$I jcl.inc}{$I windowsonly.inc}uses  {$IFDEF UNITVERSIONING}  JclUnitVersioning,  {$ENDIF UNITVERSIONING}  {$IFDEF HAS_UNITSCOPE}  {$IFDEF MSWINDOWS}  Winapi.Windows,  {$ENDIF MSWINDOWS}  System.Classes, System.SysUtils, System.Contnrs,  {$ELSE ~HAS_UNITSCOPE}  {$IFDEF MSWINDOWS}  Windows,  {$ENDIF MSWINDOWS}  Classes, SysUtils, Contnrs,  {$ENDIF ~HAS_UNITSCOPE}  JclBase, JclFileUtils, JclPeImage,  {$IFDEF BORLAND}  {$IFNDEF WINSCP}  JclTD32,  {$ENDIF ~WINSCP}  {$ENDIF BORLAND}  JclSynch;// Diagnosticsprocedure AssertKindOf(const ClassName: string; const Obj: TObject); overload;procedure AssertKindOf(const ClassType: TClass; const Obj: TObject); overload;// use TraceMsg// procedure Trace(const Msg: string);procedure TraceMsg(const Msg: string);{$IFNDEF WINSCP}procedure TraceFmt(const Fmt: string; const Args: array of const);{$ENDIF}procedure TraceLoc(const Msg: string);procedure TraceLocFmt(const Fmt: string; const Args: array of const);// Optimized functionality of JclSysInfo functions ModuleFromAddr and IsSystemModuletype  TJclModuleInfo = class(TObject)  private    FSize: Cardinal;    FEndAddr: Pointer;    FStartAddr: Pointer;    FSystemModule: Boolean;  public    property EndAddr: Pointer read FEndAddr;    property Size: Cardinal read FSize;    property StartAddr: Pointer read FStartAddr;    property SystemModule: Boolean read FSystemModule;  end;  TJclModuleInfoList = class(TObjectList)  private    FDynamicBuild: Boolean;    FSystemModulesOnly: Boolean;    FRefCount: Integer;    function GetItems(Index: TJclListSize): TJclModuleInfo;    function GetModuleFromAddress(Addr: Pointer): TJclModuleInfo;  protected    procedure BuildModulesList;    function CreateItemForAddress(Addr: Pointer; SystemModule: Boolean): TJclModuleInfo;  public    constructor Create(ADynamicBuild, ASystemModulesOnly: Boolean);    function AddModule(Module: HMODULE; SystemModule: Boolean): Boolean;    function IsSystemModuleAddress(Addr: Pointer): Boolean;    function IsValidModuleAddress(Addr: Pointer): Boolean;    property DynamicBuild: Boolean read FDynamicBuild;    property Items[Index: TJclListSize]: TJclModuleInfo read GetItems;    property ModuleFromAddress[Addr: Pointer]: TJclModuleInfo read GetModuleFromAddress;  end;function JclValidateModuleAddress(Addr: Pointer): Boolean;// MAP file abstract parsertype  PJclMapAddress = ^TJclMapAddress;  TJclMapAddress = packed record    Segment: Word;    Offset: TJclAddr;  end;  PJclMapString = PAnsiChar;  TJclAbstractMapParser = class(TObject)  private    FLinkerBug: Boolean;    FLinkerBugUnitName: PJclMapString;    FStream: TJclFileMappingStream;    function GetLinkerBugUnitName: string;  protected    FModule: HMODULE;    FLastUnitName: PJclMapString;    FLastUnitFileName: PJclMapString;    procedure ClassTableItem(const Address: TJclMapAddress; Len: Integer; SectionName, GroupName: PJclMapString); virtual; abstract;    procedure SegmentItem(const Address: TJclMapAddress; Len: Integer; GroupName, UnitName: PJclMapString); virtual; abstract;    function CanHandlePublicsByName: Boolean; virtual; abstract;    function CanHandlePublicsByValue: Boolean; virtual; abstract;    procedure PublicsByNameItem(const Address: TJclMapAddress; Name: PJclMapString); virtual; abstract;    procedure PublicsByValueItem(const Address: TJclMapAddress; Name: PJclMapString); virtual; abstract;    procedure LineNumberUnitItem(UnitName, UnitFileName: PJclMapString); virtual; abstract;    procedure LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress); virtual; abstract;  public    constructor Create(const MapFileName: TFileName; Module: HMODULE); overload; virtual;    constructor Create(const MapFileName: TFileName); overload;    destructor Destroy; override;    procedure Parse;    class function MapStringToFileName(MapString: PJclMapString): string;    class function MapStringToModuleName(MapString: PJclMapString): string;    class function MapStringToStr(MapString: PJclMapString; IgnoreSpaces: Boolean = False): string;    property LinkerBug: Boolean read FLinkerBug;    property LinkerBugUnitName: string read GetLinkerBugUnitName;    property Stream: TJclFileMappingStream read FStream;  end;  // MAP file parser  TJclMapClassTableEvent = procedure(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const SectionName, GroupName: string) of object;  TJclMapSegmentEvent = procedure(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const GroupName, UnitName: string) of object;  TJclMapPublicsEvent = procedure(Sender: TObject; const Address: TJclMapAddress; const Name: string) of object;  TJclMapLineNumberUnitEvent = procedure(Sender: TObject; const UnitName, UnitFileName: string) of object;  TJclMapLineNumbersEvent = procedure(Sender: TObject; LineNumber: Integer; const Address: TJclMapAddress) of object;  TJclMapParser = class(TJclAbstractMapParser)  private    FOnClassTable: TJclMapClassTableEvent;    FOnLineNumbers: TJclMapLineNumbersEvent;    FOnLineNumberUnit: TJclMapLineNumberUnitEvent;    FOnPublicsByValue: TJclMapPublicsEvent;    FOnPublicsByName: TJclMapPublicsEvent;    FOnSegmentItem: TJclMapSegmentEvent;  protected    procedure ClassTableItem(const Address: TJclMapAddress; Len: Integer; SectionName, GroupName: PJclMapString); override;    procedure SegmentItem(const Address: TJclMapAddress; Len: Integer; GroupName, UnitName: PJclMapString); override;    function CanHandlePublicsByName: Boolean; override;    function CanHandlePublicsByValue: Boolean; override;    procedure PublicsByNameItem(const Address: TJclMapAddress; Name: PJclMapString); override;    procedure PublicsByValueItem(const Address: TJclMapAddress; Name: PJclMapString); override;    procedure LineNumberUnitItem(UnitName, UnitFileName: PJclMapString); override;    procedure LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress); override;  public    property OnClassTable: TJclMapClassTableEvent read FOnClassTable write FOnClassTable;    property OnSegment: TJclMapSegmentEvent read FOnSegmentItem write FOnSegmentItem;    property OnPublicsByName: TJclMapPublicsEvent read FOnPublicsByName write FOnPublicsByName;    property OnPublicsByValue: TJclMapPublicsEvent read FOnPublicsByValue write FOnPublicsByValue;    property OnLineNumberUnit: TJclMapLineNumberUnitEvent read FOnLineNumberUnit write FOnLineNumberUnit;    property OnLineNumbers: TJclMapLineNumbersEvent read FOnLineNumbers write FOnLineNumbers;  end;  TJclMapStringCache = record    CachedValue: string;    RawValue: PJclMapString;    TLS: Boolean;  end;  // MAP file scanner  PJclMapSegmentClass = ^TJclMapSegmentClass;  TJclMapSegmentClass = record    Segment: Word; // segment ID    Start: DWORD;  // start as in the map file    Addr: DWORD;   // start as in process memory    VA: DWORD;     // position relative to module base adress    Len: DWORD;    // segment length    SectionName: TJclMapStringCache;    GroupName: TJclMapStringCache;  end;  PJclMapSegment = ^TJclMapSegment;  TJclMapSegment = record    Segment: Word;    StartVA: DWORD; // VA relative to (module base address + $10000)    EndVA: DWORD;    UnitName: TJclMapStringCache;  end;  PJclMapProcName = ^TJclMapProcName;  TJclMapProcName = record    Segment: Word;    VA: DWORD; // VA relative to (module base address + $10000)    ProcName: TJclMapStringCache;  end;  PJclMapLineNumber = ^TJclMapLineNumber;  TJclMapLineNumber = record    Segment: Word;    VA: DWORD; // VA relative to (module base address + $10000)    LineNumber: Integer;    UnitName: PJclMapString;  end;  TJclMapScanner = class(TJclAbstractMapParser)  private    FSegmentClasses: array of TJclMapSegmentClass;    FLineNumbers: array of TJclMapLineNumber;    FProcNames: array of TJclMapProcName;    FSegments: array of TJclMapSegment;    FSourceNames: array of TJclMapProcName;    FLineNumbersCnt: Integer;    FLineNumberErrors: Integer;    FNewUnitFileName: PJclMapString;    FCurrentUnitName: PJclMapString;    FProcNamesCnt: Integer;    FSegmentCnt: Integer;    FLastAccessedSegementIndex: Integer;    function IndexOfSegment(Addr: DWORD): Integer;  protected    function MAPAddrToVA(const Addr: DWORD): DWORD;    procedure ClassTableItem(const Address: TJclMapAddress; Len: Integer; SectionName, GroupName: PJclMapString); override;    procedure SegmentItem(const Address: TJclMapAddress; Len: Integer; GroupName, UnitName: PJclMapString); override;    function CanHandlePublicsByName: Boolean; override;    function CanHandlePublicsByValue: Boolean; override;    procedure PublicsByNameItem(const Address: TJclMapAddress; Name: PJclMapString); override;    procedure PublicsByValueItem(const Address: TJclMapAddress; Name: PJclMapString); override;    procedure LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress); override;    procedure LineNumberUnitItem(UnitName, UnitFileName: PJclMapString); override;    procedure Scan;    function GetLineNumberByIndex(Index: Integer): TJCLMapLineNumber;  public    constructor Create(const MapFileName: TFileName; Module: HMODULE); override;    class function MapStringCacheToFileName(var MapString: TJclMapStringCache): string;    class function MapStringCacheToModuleName(var MapString: TJclMapStringCache): string;    class function MapStringCacheToStr(var MapString: TJclMapStringCache; IgnoreSpaces: Boolean = False): string;    // Addr are virtual addresses relative to (module base address + $10000)    function LineNumberFromAddr(Addr: DWORD): Integer; overload;    function LineNumberFromAddr(Addr: DWORD; out Offset: Integer): Integer; overload;    function ModuleNameFromAddr(Addr: DWORD): string;    function ModuleStartFromAddr(Addr: DWORD): DWORD;    function ProcNameFromAddr(Addr: DWORD): string; overload;    function ProcNameFromAddr(Addr: DWORD; out Offset: Integer): string; overload;    function SourceNameFromAddr(Addr: DWORD): string;    function VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;    property LineNumberErrors: Integer read FLineNumberErrors;    property LineNumbersCnt: Integer read FLineNumbersCnt;    property LineNumberByIndex[Index: Integer]: TJclMapLineNumber read GetLineNumberByIndex;  end;type  PJclDbgHeader = ^TJclDbgHeader;  TJclDbgHeader = packed record    Signature: DWORD;    Version: Byte;    Units: Integer;    SourceNames: Integer;    Symbols: Integer;    LineNumbers: Integer;    Words: Integer;    ModuleName: Integer;    CheckSum: Integer;    CheckSumValid: Boolean;  end;  TJclBinDebugGenerator = class(TJclMapScanner)  private    FDataStream: TMemoryStream;    FMapFileName: TFileName;  protected    procedure CreateData;  public    constructor Create(const MapFileName: TFileName; Module: HMODULE); override;    destructor Destroy; override;    function CalculateCheckSum: Boolean;    property DataStream: TMemoryStream read FDataStream;  end;  TJclBinDbgNameCache = record    Addr: DWORD;    FirstWord: Integer;    SecondWord: Integer;    Text: string;  end;  TJclBinDebugScanner = class(TObject)  private    FCacheData: Boolean;    FCacheProcNames: Boolean;    FStream: TCustomMemoryStream;    FValidFormat: Boolean;    FLineNumbers: array of TJclMapLineNumber;    FProcNames: array of TJclBinDbgNameCache;    function GetModuleName: string;  protected    procedure CacheLineNumbers;    procedure CacheProcNames;    procedure CheckFormat;    function DataToStr(A: Integer): string;    function MakePtr(A: Integer): Pointer;    class function ReadValue(var P: Pointer; var Value: Integer): Boolean; {$IFDEF SUPPORTS_STATIC}static;{$ENDIF}  public    constructor Create(AStream: TCustomMemoryStream; CacheData, CacheProcNames: Boolean);    function IsModuleNameValid(const Name: TFileName): Boolean;    function LineNumberFromAddr(Addr: DWORD): Integer; overload;    function LineNumberFromAddr(Addr: DWORD; out Offset: Integer): Integer; overload;    function ProcNameFromAddr(Addr: DWORD): string; overload;    function ProcNameFromAddr(Addr: DWORD; out Offset: Integer): string; overload;    function ModuleNameFromAddr(Addr: DWORD): string;    function ModuleStartFromAddr(Addr: DWORD): DWORD;    function SourceNameFromAddr(Addr: DWORD): string;    property ModuleName: string read GetModuleName;    property ValidFormat: Boolean read FValidFormat;    function VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;  end;function ConvertMapFileToJdbgFile(const MapFileName: TFileName): Boolean; overload;function ConvertMapFileToJdbgFile(const MapFileName: TFileName; out LinkerBugUnit: string;  out LineNumberErrors: Integer): Boolean; overload;function ConvertMapFileToJdbgFile(const MapFileName: TFileName; out LinkerBugUnit: string;  out LineNumberErrors, MapFileSize, JdbgFileSize: Integer): Boolean; overload;function InsertDebugDataIntoExecutableFile(const ExecutableFileName,  MapFileName: TFileName; out LinkerBugUnit: string;  out MapFileSize, JclDebugDataSize: Integer): Boolean; overload;function InsertDebugDataIntoExecutableFile(const ExecutableFileName,  MapFileName: TFileName; out LinkerBugUnit: string;  out MapFileSize, JclDebugDataSize, LineNumberErrors: Integer): Boolean; overload;function InsertDebugDataIntoExecutableFile(const ExecutableFileName: TFileName;  BinDebug: TJclBinDebugGenerator; out LinkerBugUnit: string;  out MapFileSize, JclDebugDataSize: Integer): Boolean; overload;function InsertDebugDataIntoExecutableFile(const ExecutableFileName: TFileName;  BinDebug: TJclBinDebugGenerator; out LinkerBugUnit: string;  out MapFileSize, JclDebugDataSize, LineNumberErrors: Integer): Boolean; overload;// Source Locationstype  TJclDebugInfoSource = class;  PJclLocationInfo = ^TJclLocationInfo;  TJclLocationInfo = record    Address: Pointer;               // Error address    UnitName: string;               // Name of Delphi unit    ProcedureName: string;          // Procedure name    OffsetFromProcName: Integer;    // Offset from Address to ProcedureName symbol location    LineNumber: Integer;            // Line number    OffsetFromLineNumber: Integer;  // Offset from Address to LineNumber symbol location    SourceName: string;             // Module file name    DebugInfo: TJclDebugInfoSource; // Location object    BinaryFileName: string;         // Name of the binary file containing the symbol  end;  TJclLocationInfoExValues = set of (lievLocationInfo, lievProcedureStartLocationInfo, lievUnitVersionInfo);  TJclCustomLocationInfoList = class;  TJclLocationInfoListOptions = set of (liloAutoGetAddressInfo, liloAutoGetLocationInfo, liloAutoGetUnitVersionInfo);  TJclLocationInfoEx = class(TPersistent)  private    FAddress: Pointer;    FBinaryFileName: string;    FDebugInfo: TJclDebugInfoSource;    FLineNumber: Integer;    FLineNumberOffsetFromProcedureStart: Integer;    FModuleName: string;    FOffsetFromLineNumber: Integer;    FOffsetFromProcName: Integer;    FParent: TJclCustomLocationInfoList;    FProcedureName: string;    FSourceName: string;    FSourceUnitName: string;    FUnitVersionDateTime: TDateTime;    FUnitVersionExtra: string;    FUnitVersionLogPath: string;    FUnitVersionRCSfile: string;    FUnitVersionRevision: string;    FVAddress: Pointer;    FValues: TJclLocationInfoExValues;    procedure Fill(AOptions: TJclLocationInfoListOptions);    function GetAsString: string;  protected    procedure AssignTo(Dest: TPersistent); override;  public    constructor Create(AParent: TJclCustomLocationInfoList; Address: Pointer);    procedure Clear; virtual;    property Address: Pointer read FAddress write FAddress;    property AsString: string read GetAsString;    property BinaryFileName: string read FBinaryFileName write FBinaryFileName;    property DebugInfo: TJclDebugInfoSource read FDebugInfo write FDebugInfo;    property LineNumber: Integer read FLineNumber write FLineNumber;    property LineNumberOffsetFromProcedureStart: Integer read FLineNumberOffsetFromProcedureStart write FLineNumberOffsetFromProcedureStart;    property ModuleName: string read FModuleName write FModuleName;    property OffsetFromLineNumber: Integer read FOffsetFromLineNumber write FOffsetFromLineNumber;    property OffsetFromProcName: Integer read FOffsetFromProcName write FOffsetFromProcName;    property ProcedureName: string read FProcedureName write FProcedureName;    property SourceName: string read FSourceName write FSourceName;    { this is equal to TJclLocationInfo.UnitName, but has been renamed because      UnitName is a class function in TObject since Delphi 2009 }    property SourceUnitName: string read FSourceUnitName write FSourceUnitName;    property UnitVersionDateTime: TDateTime read FUnitVersionDateTime write FUnitVersionDateTime;    property UnitVersionExtra: string read FUnitVersionExtra write FUnitVersionExtra;    property UnitVersionLogPath: string read FUnitVersionLogPath write FUnitVersionLogPath;    property UnitVersionRCSfile: string read FUnitVersionRCSfile write FUnitVersionRCSfile;    property UnitVersionRevision: string read FUnitVersionRevision write FUnitVersionRevision;    property VAddress: Pointer read FVAddress write FVAddress;    property Values: TJclLocationInfoExValues read FValues write FValues;  end;  TJclLocationInfoClass = class of TJclLocationInfoEx;  TJclCustomLocationInfoListClass = class of TJclCustomLocationInfoList;  TJclCustomLocationInfoList = class(TPersistent)  protected    FItemClass: TJclLocationInfoClass;    FItems: TObjectList;    FOptions: TJclLocationInfoListOptions;    function GetAsString: string;    function GetCount: Integer;    function InternalAdd(Addr: Pointer): TJclLocationInfoEx;  protected    procedure AssignTo(Dest: TPersistent); override;  public    constructor Create; virtual;    destructor Destroy; override;    procedure AddStackInfoList(AStackInfoList: TObject);    procedure Clear;    property AsString: string read GetAsString;    property Count: Integer read GetCount;    property Options: TJclLocationInfoListOptions read FOptions write FOptions;  end;  TJclLocationInfoList = class(TJclCustomLocationInfoList)  private    function GetItems(AIndex: Integer): TJclLocationInfoEx;  public    constructor Create; override;    function Add(Addr: Pointer): TJclLocationInfoEx;    property Items[AIndex: Integer]: TJclLocationInfoEx read GetItems; default;  end;  TJclDebugInfoSource = class(TObject)  private    FModule: HMODULE;    FModuleCodeSize: SizeInt;    function GetFileName: TFileName;  protected    function VAFromAddr(const Addr: Pointer): DWORD; virtual;    function AddrFromVA(const VA: DWORD): Pointer; virtual;  public    constructor Create(AModule: HMODULE); virtual;    function InitializeSource: Boolean; virtual; abstract;    function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; virtual; abstract;    function GetAddress(const UnitName, ProcName: string): Pointer; virtual; abstract;    property Module: HMODULE read FModule;    property FileName: TFileName read GetFileName;    property ModuleCodeSize: SizeInt read FModuleCodeSize;  end;  TJclDebugInfoSourceClass = class of TJclDebugInfoSource;  TJclDebugInfoList = class(TObjectList)  private    function GetItemFromModule(const Module: HMODULE): TJclDebugInfoSource;    function GetItems(Index: TJclListSize): TJclDebugInfoSource;  protected    function CreateDebugInfo(const Module: HMODULE): TJclDebugInfoSource;  public    class procedure RegisterDebugInfoSource(      const InfoSourceClass: TJclDebugInfoSourceClass);    class procedure UnRegisterDebugInfoSource(      const InfoSourceClass: TJclDebugInfoSourceClass);    class procedure RegisterDebugInfoSourceFirst(      const InfoSourceClass: TJclDebugInfoSourceClass);    class procedure NeedInfoSourceClassList;    function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;    property ItemFromModule[const Module: HMODULE]: TJclDebugInfoSource read GetItemFromModule;    property Items[Index: TJclListSize]: TJclDebugInfoSource read GetItems;  end;  // Various source location implementations  TJclDebugInfoMap = class(TJclDebugInfoSource)  private    FScanner: TJclMapScanner;  public    destructor Destroy; override;    function InitializeSource: Boolean; override;    function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;    function GetAddress(const UnitName, ProcName: string): Pointer; override;  end;  TJclDebugInfoBinary = class(TJclDebugInfoSource)  private    FScanner: TJclBinDebugScanner;    FStream: TCustomMemoryStream;  public    destructor Destroy; override;    function InitializeSource: Boolean; override;    function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;    function GetAddress(const UnitName, ProcName: string): Pointer; override;  end;  TJclDebugInfoExports = class(TJclDebugInfoSource)  private    {$IFDEF BORLAND}    FImage: TJclPeBorImage;    {$ENDIF BORLAND}    {$IFDEF FPC}    FImage: TJclPeImage;    {$ENDIF FPC}    function IsAddressInThisExportedFunction(Addr: PByteArray; FunctionStartAddr: TJclAddr): Boolean;  public    destructor Destroy; override;    function InitializeSource: Boolean; override;    function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;    function GetAddress(const UnitName, ProcName: string): Pointer; override;  end;  {$IFDEF BORLAND}  {$IFNDEF WINSCP}  TJclDebugInfoTD32 = class(TJclDebugInfoSource)  private    FImage: TJclPeBorTD32Image;  public    destructor Destroy; override;    function InitializeSource: Boolean; override;    procedure GenerateUnmangledNames;    function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;    function GetAddress(const UnitName, ProcName: string): Pointer; override;  end;  {$ENDIF ~WINSCP}  {$ENDIF BORLAND}  TJclDebugInfoSymbols = class(TJclDebugInfoSource)  public    class function LoadDebugFunctions: Boolean;    class function UnloadDebugFunctions: Boolean;    class function InitializeDebugSymbols: Boolean;    class function CleanupDebugSymbols: Boolean;    function InitializeSource: Boolean; override;    function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; override;    function GetAddress(const UnitName, ProcName: string): Pointer; override;  end;// Source location functionsfunction Caller(Level: Integer = 0; FastStackWalk: Boolean = False): Pointer;procedure BeginGetLocationInfoCache;procedure EndGetLocationInfoCache;function GetLocationInfo(const Addr: Pointer): TJclLocationInfo; overload;function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean; overload;function GetLocationInfoStr(const Addr: Pointer; IncludeModuleName: Boolean = False;  IncludeAddressOffset: Boolean = False; IncludeStartProcLineOffset: Boolean = False;  IncludeVAddress: Boolean = False): string;function DebugInfoAvailable(const Module: HMODULE): Boolean;procedure ClearLocationData;function FileByLevel(const Level: Integer = 0): string;function ModuleByLevel(const Level: Integer = 0): string;function ProcByLevel(const Level: Integer = 0; OnlyProcedureName: boolean =false): string;function LineByLevel(const Level: Integer = 0): Integer;function MapByLevel(const Level: Integer; var File_, Module_, Proc_: string; var Line_: Integer): Boolean;function FileOfAddr(const Addr: Pointer): string;function ModuleOfAddr(const Addr: Pointer): string;function ProcOfAddr(const Addr: Pointer): string;function LineOfAddr(const Addr: Pointer): Integer;function MapOfAddr(const Addr: Pointer; var File_, Module_, Proc_: string; var Line_: Integer): Boolean;function ExtractClassName(const ProcedureName: string): string;function ExtractMethodName(const ProcedureName: string): string;// Original function names, deprecated will be removed in V2.0; do not use!function __FILE__(const Level: Integer = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __MODULE__(const Level: Integer = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __PROC__(const Level: Integer  = 0): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __LINE__(const Level: Integer = 0): Integer; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __MAP__(const Level: Integer; var _File, _Module, _Proc: string; var _Line: Integer): Boolean; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __FILE_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __MODULE_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __PROC_OF_ADDR__(const Addr: Pointer): string; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __LINE_OF_ADDR__(const Addr: Pointer): Integer; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}function __MAP_OF_ADDR__(const Addr: Pointer; var _File, _Module, _Proc: string;  var _Line: Integer): Boolean; {$IFDEF SUPPORTS_DEPRECATED} deprecated; {$ENDIF}// Stack info routines base listtype  TJclStackBaseList = class(TObjectList)  private    FThreadID: DWORD;    FTimeStamp: TDateTime;  protected    FOnDestroy: TNotifyEvent;  public    constructor Create;    destructor Destroy; override;    property ThreadID: DWORD read FThreadID;    property TimeStamp: TDateTime read FTimeStamp;  end;// Stack info routinestype  PDWORD_PTRArray = ^TDWORD_PTRArray;  TDWORD_PTRArray = array [0..(MaxInt - $F) div SizeOf(DWORD_PTR)] of DWORD_PTR;  {$IFNDEF FPC}  PDWORD_PTR = ^DWORD_PTR;  {$ENDIF ~FPC}  PStackFrame = ^TStackFrame;  TStackFrame = record    CallerFrame: TJclAddr;    CallerAddr: TJclAddr;  end;  PStackInfo = ^TStackInfo;  TStackInfo = record    CallerAddr: TJclAddr;    Level: Integer;    CallerFrame: TJclAddr;    DumpSize: DWORD;    ParamSize: DWORD;    ParamPtr: PDWORD_PTRArray;    case Integer of      0:        (StackFrame: PStackFrame);      1:        (DumpPtr: PJclByteArray);  end;  TJclStackInfoItem = class(TObject)  private    FStackInfo: TStackInfo;    function GetCallerAddr: Pointer;    function GetLogicalAddress: TJclAddr;  public    property CallerAddr: Pointer read GetCallerAddr;    property LogicalAddress: TJclAddr read GetLogicalAddress;    property StackInfo: TStackInfo read FStackInfo;  end;  TJclStackInfoList = class(TJclStackBaseList)  private    FIgnoreLevels: Integer;    TopOfStack: TJclAddr;    BaseOfStack: TJclAddr;    FStackData: PPointer;    FFramePointer: Pointer;    FModuleInfoList: TJclModuleInfoList;    FCorrectOnAccess: Boolean;    FSkipFirstItem: Boolean;    FDelayedTrace: Boolean;    FInStackTracing: Boolean;    FRaw: Boolean;    FStackOffset: Int64;    {$IFDEF CPU64}    procedure CaptureBackTrace;    {$ENDIF CPU64}    function GetItems(Index: TJclListSize): TJclStackInfoItem;    function NextStackFrame(var StackFrame: PStackFrame; var StackInfo: TStackInfo): Boolean;    procedure StoreToList(const StackInfo: TStackInfo);    procedure TraceStackFrames;    procedure TraceStackRaw;    {$IFDEF CPU32}    procedure DelayStoreStack;    {$ENDIF CPU32}    function ValidCallSite(CodeAddr: TJclAddr; out CallInstructionSize: Cardinal): Boolean;    function ValidStackAddr(StackAddr: TJclAddr): Boolean;    function GetCount: Integer;    procedure CorrectOnAccess(ASkipFirstItem: Boolean);  public    constructor Create(ARaw: Boolean; AIgnoreLevels: Integer;      AFirstCaller: Pointer); overload;    constructor Create(ARaw: Boolean; AIgnoreLevels: Integer;      AFirstCaller: Pointer; ADelayedTrace: Boolean); overload;    constructor Create(ARaw: Boolean; AIgnoreLevels: Integer;      AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack: Pointer); overload;    constructor Create(ARaw: Boolean; AIgnoreLevels: Integer;      AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack, ATopOfStack: Pointer); overload;    destructor Destroy; override;    procedure ForceStackTracing;    procedure AddToStrings(Strings: TStrings; IncludeModuleName: Boolean = False;      IncludeAddressOffset: Boolean = False; IncludeStartProcLineOffset: Boolean = False;      IncludeVAddress: Boolean = False);    property DelayedTrace: Boolean read FDelayedTrace;    property Items[Index: TJclListSize]: TJclStackInfoItem read GetItems; default;    property IgnoreLevels: Integer read FIgnoreLevels;    property Count: Integer read GetCount;    property Raw: Boolean read FRaw;  end;{$IFDEF WINSCP}procedure DoExceptionStackTrace(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean;  BaseOfStack: Pointer);procedure DoExceptFrameTrace;{$ENDIF}function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer): TJclStackInfoList; overload;function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;  DelayedTrace: Boolean): TJclStackInfoList; overload;function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;  DelayedTrace: Boolean; BaseOfStack: Pointer): TJclStackInfoList; overload;function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;  DelayedTrace: Boolean; BaseOfStack, TopOfStack: Pointer): TJclStackInfoList; overload;function JclCreateThreadStackTrace(Raw: Boolean; const ThreadHandle: THandle): TJclStackInfoList;function JclCreateThreadStackTraceFromID(Raw: Boolean; ThreadID: DWORD): TJclStackInfoList;function JclLastExceptStackList: TJclStackInfoList;function JclLastExceptStackListToStrings(Strings: TStrings; IncludeModuleName: Boolean = False;  IncludeAddressOffset: Boolean = False; IncludeStartProcLineOffset: Boolean = False;  IncludeVAddress: Boolean = False): Boolean;function JclGetExceptStackList(ThreadID: DWORD): TJclStackInfoList;function JclGetExceptStackListToStrings(ThreadID: DWORD; Strings: TStrings;  IncludeModuleName: Boolean = False; IncludeAddressOffset: Boolean = False;  IncludeStartProcLineOffset: Boolean = False; IncludeVAddress: Boolean = False): Boolean;// helper function for DUnit runtime memory leak checkprocedure JclClearGlobalStackData;// Exception frame info routinestype  PJmpInstruction = ^TJmpInstruction;  TJmpInstruction = packed record // from System.pas    OpCode: Byte;    Distance: Longint;  end;  TExcDescEntry = record // from System.pas    VTable: Pointer;    Handler: Pointer;  end;  PExcDesc = ^TExcDesc;  TExcDesc = packed record // from System.pas    JMP: TJmpInstruction;    case Integer of      0:        (Instructions: array [0..0] of Byte);      1:       (Cnt: Integer;        ExcTab: array [0..0] of TExcDescEntry);  end;  PExcFrame = ^TExcFrame;  TExcFrame =  record // from System.pas    Next: PExcFrame;    Desc: PExcDesc;    FramePointer: Pointer;    case Integer of      0:        ();      1:        (ConstructedObject: Pointer);      2:        (SelfOfMethod: Pointer);  end;  PJmpTable = ^TJmpTable;  TJmpTable = packed record    OPCode: Word; // FF 25 = JMP DWORD PTR [$xxxxxxxx], encoded as $25FF    Ptr: Pointer;  end;  TExceptFrameKind =    (efkUnknown, efkFinally, efkAnyException, efkOnException, efkAutoException);  TJclExceptFrame = class(TObject)  private    FFrameKind: TExceptFrameKind;    FFrameLocation: Pointer;    FCodeLocation: Pointer;    FExcTab: array of TExcDescEntry;  protected    procedure AnalyseExceptFrame(AExcDesc: PExcDesc);  public    constructor Create(AFrameLocation: Pointer; AExcDesc: PExcDesc);    function Handles(ExceptObj: TObject): Boolean;    function HandlerInfo(ExceptObj: TObject; out HandlerAt: Pointer): Boolean;    property CodeLocation: Pointer read FCodeLocation;    property FrameLocation: Pointer read FFrameLocation;    property FrameKind: TExceptFrameKind read FFrameKind;  end;  TJclExceptFrameList = class(TJclStackBaseList)  private    FIgnoreLevels: Integer;    function GetItems(Index: TJclListSize): TJclExceptFrame;  protected    function AddFrame(AFrame: PExcFrame): TJclExceptFrame;  public    constructor Create(AIgnoreLevels: Integer);    procedure TraceExceptionFrames;    property Items[Index: TJclListSize]: TJclExceptFrame read GetItems;    property IgnoreLevels: Integer read FIgnoreLevels write FIgnoreLevels;  end;function JclCreateExceptFrameList(AIgnoreLevels: Integer): TJclExceptFrameList;function JclLastExceptFrameList: TJclExceptFrameList;function JclGetExceptFrameList(ThreadID: DWORD): TJclExceptFrameList;function JclStartExceptionTracking: Boolean;function JclStopExceptionTracking: Boolean;function JclExceptionTrackingActive: Boolean;function JclTrackExceptionsFromLibraries: Boolean;// Thread exception tracking supporttype  TJclDebugThread = class(TThread)  private    FSyncException: TObject;    FThreadName: string;    procedure DoHandleException;    function GetThreadInfo: string;  protected    procedure DoNotify;    procedure DoSyncHandleException; dynamic;    procedure HandleException(Sender: TObject = nil);  public    constructor Create(ASuspended: Boolean; const AThreadName: string = '');    destructor Destroy; override;    property SyncException: TObject read FSyncException;    property ThreadInfo: string read GetThreadInfo;    property ThreadName: string read FThreadName;  end;  TJclDebugThreadNotifyEvent = procedure(Thread: TJclDebugThread) of object;  TJclThreadIDNotifyEvent = procedure(ThreadID: DWORD) of object;  TJclDebugThreadList = class(TObject)  private    FList: TObjectList;    FLock: TJclCriticalSection;    FReadLock: TJclCriticalSection;    FRegSyncThreadID: DWORD;    FSaveCreationStack: Boolean;    FUnregSyncThreadID: DWORD;    FOnSyncException: TJclDebugThreadNotifyEvent;    FOnThreadRegistered: TJclThreadIDNotifyEvent;    FOnThreadUnregistered: TJclThreadIDNotifyEvent;    function GetThreadClassNames(ThreadID: DWORD): string;    function GetThreadInfos(ThreadID: DWORD): string;    function GetThreadNames(ThreadID: DWORD): string;    procedure DoSyncThreadRegistered;    procedure DoSyncThreadUnregistered;    function GetThreadCreationTime(ThreadID: DWORD): TDateTime;    function GetThreadHandle(Index: Integer): THandle;    function GetThreadID(Index: Integer): DWORD;    function GetThreadIDCount: Integer;    function GetThreadParentID(ThreadID: DWORD): DWORD;    function GetThreadValues(ThreadID: DWORD; Index: Integer): string;    function IndexOfThreadID(ThreadID: DWORD): Integer;  protected    procedure DoSyncException(Thread: TJclDebugThread);    procedure DoThreadRegistered(Thread: TThread);    procedure DoThreadUnregistered(Thread: TThread);    procedure InternalRegisterThread(Thread: TThread; ThreadID: DWORD; const ThreadName: string);    procedure InternalUnregisterThread(Thread: TThread; ThreadID: DWORD);  public    constructor Create;    destructor Destroy; override;    function AddStackListToLocationInfoList(ThreadID: DWORD; AList: TJclLocationInfoList): Boolean;    procedure RegisterThread(Thread: TThread; const ThreadName: string);    procedure RegisterThreadID(AThreadID: DWORD; const ThreadName: string = '');    procedure UnregisterThread(Thread: TThread);    procedure UnregisterThreadID(AThreadID: DWORD);    property Lock: TJclCriticalSection read FLock;    //property ThreadClassNames[ThreadID: DWORD]: string index 1 read GetThreadValues;    property SaveCreationStack: Boolean read FSaveCreationStack write FSaveCreationStack;    property ThreadClassNames[ThreadID: DWORD]: string read GetThreadClassNames;    property ThreadCreationTime[ThreadID: DWORD]: TDateTime read GetThreadCreationTime;    property ThreadHandles[Index: Integer]: THandle read GetThreadHandle;    property ThreadIDs[Index: Integer]: DWORD read GetThreadID;    property ThreadIDCount: Integer read GetThreadIDCount;    //property ThreadInfos[ThreadID: DWORD]: string index 2 read GetThreadValues;    property ThreadInfos[ThreadID: DWORD]: string read GetThreadInfos;    //property ThreadNames[ThreadID: DWORD]: string index 0 read GetThreadValues;    property ThreadNames[ThreadID: DWORD]: string read GetThreadNames;    property ThreadParentIDs[ThreadID: DWORD]: DWORD read GetThreadParentID;    property OnSyncException: TJclDebugThreadNotifyEvent read FOnSyncException write FOnSyncException;    property OnThreadRegistered: TJclThreadIDNotifyEvent read FOnThreadRegistered write FOnThreadRegistered;    property OnThreadUnregistered: TJclThreadIDNotifyEvent read FOnThreadUnregistered write FOnThreadUnregistered;  end;  TJclDebugThreadInfo = class(TObject)  private    FCreationTime: TDateTime;    FParentThreadID: DWORD;    FStackList: TJclStackInfoList;    FThreadClassName: string;    FThreadID: DWORD;    FThreadHandle: THandle;    FThreadName: string;  public    constructor Create(AParentThreadID, AThreadID: DWORD; AStack: Boolean);    destructor Destroy; override;    property CreationTime: TDateTime read FCreationTime;    property ParentThreadID: DWORD read FParentThreadID;    property StackList: TJclStackInfoList read FStackList;    property ThreadClassName: string read FThreadClassName write FThreadClassName;    property ThreadID: DWORD read FThreadID;    property ThreadHandle: THandle read FThreadHandle write FThreadHandle;    property ThreadName: string read FThreadName write FThreadName;  end;  TJclThreadInfoOptions = set of (tioIsMainThread, tioName, tioCreationTime, tioParentThreadID, tioStack, tioCreationStack);  TJclCustomThreadInfo = class(TPersistent)  protected    FCreationTime: TDateTime;    FCreationStack: TJclCustomLocationInfoList;    FName: string;    FParentThreadID: DWORD;    FStack: TJclCustomLocationInfoList;    FThreadID: DWORD;    FValues: TJclThreadInfoOptions;    procedure AssignTo(Dest: TPersistent); override;    function GetStackClass: TJclCustomLocationInfoListClass; virtual;  public    constructor Create;    destructor Destroy; override;    property CreationTime: TDateTime read FCreationTime write FCreationTime;    property Name: string read FName write FName;    property ParentThreadID: DWORD read FParentThreadID write FParentThreadID;    property ThreadID: DWORD read FThreadID write FThreadID;    property Values: TJclThreadInfoOptions read FValues write FValues;  end;  TJclThreadInfo = class(TJclCustomThreadInfo)  private    function GetAsString: string;    procedure InternalFill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions; AExceptThread: Boolean);    function GetStack(const AIndex: Integer): TJclLocationInfoList;  protected    function GetStackClass: TJclCustomLocationInfoListClass; override;  public    procedure Fill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions);    procedure FillFromExceptThread(AGatherOptions: TJclThreadInfoOptions);    property AsString: string read GetAsString;    property CreationStack: TJclLocationInfoList index 1 read GetStack;    property Stack: TJclLocationInfoList index 2 read GetStack;  end;  TJclThreadInfoList = class(TPersistent)  private    FGatherOptions: TJclThreadInfoOptions;    FItems: TObjectList;    function GetAsString: string;    function GetCount: Integer;    function GetItems(AIndex: Integer): TJclThreadInfo;    procedure InternalGather(AIncludeThreadIDs, AExcludeThreadIDs: array of DWORD);  protected    procedure AssignTo(Dest: TPersistent); override;  public    constructor Create;    destructor Destroy; override;    function Add: TJclThreadInfo;    procedure Clear;    procedure Gather(AExceptThreadID: DWORD);    procedure GatherExclude(AThreadIDs: array of DWORD);    procedure GatherInclude(AThreadIDs: array of DWORD);    property AsString: string read GetAsString;    property Count: Integer read GetCount;    property GatherOptions: TJclThreadInfoOptions read FGatherOptions write FGatherOptions;    property Items[AIndex: Integer]: TJclThreadInfo read GetItems; default;  end;function JclDebugThreadList: TJclDebugThreadList;function JclHookThreads: Boolean;function JclUnhookThreads: Boolean;function JclThreadsHooked: Boolean;// Miscellanuous{$IFDEF MSWINDOWS}{$IFNDEF WINSCP}function EnableCrashOnCtrlScroll(const Enable: Boolean): Boolean;{$ENDIF ~WINSCP}function IsDebuggerAttached: Boolean;function IsHandleValid(Handle: THandle): Boolean;{$ENDIF MSWINDOWS}{$IFDEF SUPPORTS_EXTSYM}{$EXTERNALSYM __FILE__}{$EXTERNALSYM __LINE__}{$ENDIF SUPPORTS_EXTSYM}const  EnvironmentVarNtSymbolPath = '_NT_SYMBOL_PATH';                    // do not localize  EnvironmentVarAlternateNtSymbolPath = '_NT_ALTERNATE_SYMBOL_PATH'; // do not localize  MaxStackTraceItems = 4096;// JCL binary debug data generator and scannerconst  JclDbgDataSignature = $4742444A; // JDBG  JclDbgDataResName   = AnsiString('JCLDEBUG'); // do not localize  JclDbgHeaderVersion = 1; // JCL 1.11 and 1.20  JclDbgFileExtension = '.jdbg'; // do not localize  JclMapFileExtension = '.map';  // do not localize  DrcFileExtension = '.drc';  // do not localize// Global exceptional stack tracker enable routines and variablestype  TJclStackTrackingOption =    (stStack, stExceptFrame, stRawMode, stAllModules, stStaticModuleList,     stDelayedTrace, stTraceAllExceptions, stMainThreadOnly, stDisableIfDebuggerAttached     {$IFDEF HAS_EXCEPTION_STACKTRACE}     // Resolves the Exception.Stacktrace string when the exception is raised. This is more     // exact if modules are unloaded before the delayed resolving happens, but it slows down     // the exception handling if no stacktrace is needed for the exception.     , stImmediateExceptionStacktraceResolving     {$ENDIF HAS_EXCEPTION_STACKTRACE}     // stCleanRawStack does a deeper analysis of the callstack by evaluating the instructions     // that manipulate the stack.     // It removes many cases of false positives but may also remove valid entries if it runs     // into a function that does non-standard stack pointer manipulation.     , stCleanRawStack // experimental    );  TJclStackTrackingOptions = set of TJclStackTrackingOption;  {$IFDEF HAS_EXCEPTION_STACKTRACE}  TJclExceptionStacktraceOption = (    estoIncludeModuleName,    estoIncludeAdressOffset,    estoIncludeStartProcLineOffset,    estoIncludeVAddress    );  TJclExceptionStacktraceOptions = set of TJclExceptionStacktraceOption;  {$ENDIF HAS_EXCEPTION_STACKTRACE}var  JclStackTrackingOptions: TJclStackTrackingOptions = [stStack];  {$IFDEF HAS_EXCEPTION_STACKTRACE}  // JclExceptionStacktraceOptions controls the Exception.Stacktrace string's format  JclExceptionStacktraceOptions: TJclExceptionStacktraceOptions =    [estoIncludeModuleName, estoIncludeAdressOffset, estoIncludeStartProcLineOffset, estoIncludeVAddress];  {$ENDIF HAS_EXCEPTION_STACKTRACE}  { JclDebugInfoSymbolPaths specifies a list of paths, separated by ';', in    which the DebugInfoSymbol scanner should look for symbol information. }  JclDebugInfoSymbolPaths: string = '';// functions to add/remove exception classes to be ignored if StTraceAllExceptions is not setprocedure AddIgnoredException(const ExceptionClass: TClass);procedure AddIgnoredExceptionByName(const AExceptionClassName: string);procedure RemoveIgnoredException(const ExceptionClass: TClass);procedure RemoveIgnoredExceptionByName(const AExceptionClassName: string);function IsIgnoredException(const ExceptionClass: TClass): Boolean;// function to add additional system modules to be included in the stack traceprocedure AddModule(const ModuleName: string);{$IFDEF UNITVERSIONING}const  UnitVersioning: TUnitVersionInfo = (    RCSfile: '$URL$';    Revision: '$Revision$';    Date: '$Date$';    LogPath: 'JCL\source\windows';    Extra: '';    Data: nil    );{$ENDIF UNITVERSIONING}implementationuses  {$IFDEF HAS_UNITSCOPE}  System.RTLConsts,  System.Types, // for inlining TList.Remove  {$IFDEF HAS_UNIT_CHARACTER}  System.Character,  {$ENDIF HAS_UNIT_CHARACTER}  {$IFDEF SUPPORTS_GENERICS}  System.Generics.Collections,  {$ENDIF SUPPORTS_GENERICS}  {$ELSE ~HAS_UNITSCOPE}  RTLConsts,  {$IFDEF HAS_UNIT_CHARACTER}  Character,  {$ENDIF HAS_UNIT_CHARACTER}  {$IFDEF SUPPORTS_GENERICS}  Generics.Collections,  {$ENDIF SUPPORTS_GENERICS}  {$ENDIF ~HAS_UNITSCOPE}  {$IFDEF MSWINDOWS}  {$IFNDEF WINSCP}  JclRegistry,  {$ELSE}  System.AnsiStrings,  {$ENDIF ~WINSCP}  {$ENDIF MSWINDOWS}  JclHookExcept, JclAnsiStrings, JclStrings, JclSysInfo, JclSysUtils, JclWin32,  {$IFNDEF WINSCP}JclStringConversions,{$ENDIF ~WINSCP} JclResources;//=== Helper assembler routines ==============================================const  ModuleCodeOffset = $1000;var  HexMap: array[AnsiChar] of Byte;  JclDebugFinalized: Boolean;  GlobalStackListLiveCount: Integer;procedure FreeJclDebugGlobals;  forward;{$STACKFRAMES OFF}function GetFramePointer: Pointer;asm        {$IFDEF CPU32}        MOV     EAX, EBP        {$ENDIF CPU32}        {$IFDEF CPU64}        MOV     RAX, RBP        {$ENDIF CPU64}end;function GetStackPointer: Pointer;asm        {$IFDEF CPU32}        MOV     EAX, ESP        {$ENDIF CPU32}        {$IFDEF CPU64}        MOV     RAX, RSP        {$ENDIF CPU64}end;{$IFDEF CPU32}function GetExceptionPointer: Pointer;asm        XOR     EAX, EAX        MOV     EAX, FS:[EAX]end;{$ENDIF CPU32}// Reference: Matt Pietrek, MSJ, Under the hood, on TIBs:// http://www.microsoft.com/MSJ/archive/S2CE.HTMfunction GetStackTop: TJclAddr;asm        {$IFDEF CPU32}        MOV     EAX, FS:[0].NT_TIB32.StackBase        {$ENDIF CPU32}        {$IFDEF CPU64}        {$IFDEF DELPHI64_TEMPORARY}        //TODO: check if the FS version doesn't work in general in 64-bit mode        MOV     RAX, GS:[ABS 8]        {$ELSE ~DELPHI64_TEMPORARY}        MOV     RAX, FS:[0].NT_TIB64.StackBase        {$ENDIF ~DELPHI64_TEMPORARY}        {$ENDIF CPU64}end;{$IFDEF STACKFRAMES_ON}{$STACKFRAMES ON}{$ENDIF STACKFRAMES_ON}//===  Diagnostics ===========================================================procedure AssertKindOf(const ClassName: string; const Obj: TObject);var  C: TClass;begin  if not Obj.ClassNameIs(ClassName) then  begin    C := Obj.ClassParent;    while (C <> nil) and (not C.ClassNameIs(ClassName)) do      C := C.ClassParent;    Assert(C <> nil);  end;end;procedure AssertKindOf(const ClassType: TClass; const Obj: TObject);begin  Assert(Obj.InheritsFrom(ClassType));end;procedure TraceMsg(const Msg: string);begin  OutputDebugString(PChar(StrDoubleQuote(Msg)));end;{$IFNDEF WINSCP}procedure TraceFmt(const Fmt: string; const Args: array of const);begin  OutputDebugString(PChar(Format(StrDoubleQuote(Fmt), Args)));end;{$ENDIF}procedure TraceLoc(const Msg: string);begin  OutputDebugString(PChar(Format('%s:%u (%s) "%s"',    [FileByLevel(1), LineByLevel(1), ProcByLevel(1), Msg])));end;procedure TraceLocFmt(const Fmt: string; const Args: array of const);var  S: string;begin  S := Format('%s:%u (%s) ', [FileByLevel(1), LineByLevel(1), ProcByLevel(1)]) +    Format(StrDoubleQuote(Fmt), Args);  OutputDebugString(PChar(S));end;//=== { TJclModuleInfoList } =================================================constructor TJclModuleInfoList.Create(ADynamicBuild, ASystemModulesOnly: Boolean);begin  inherited Create(True);  FDynamicBuild := ADynamicBuild;  FSystemModulesOnly := ASystemModulesOnly;  if not FDynamicBuild then    BuildModulesList;end;function TJclModuleInfoList.AddModule(Module: HMODULE; SystemModule: Boolean): Boolean;begin  Result := not IsValidModuleAddress(Pointer(Module)) and    (CreateItemForAddress(Pointer(Module), SystemModule) <> nil);end;{function SortByStartAddress(Item1, Item2: Pointer): Integer;begin  Result := INT_PTR(TJclModuleInfo(Item2).StartAddr) - INT_PTR(TJclModuleInfo(Item1).StartAddr);end;}procedure TJclModuleInfoList.BuildModulesList;var  List: TStringList;  I: Integer;  CurModule: PLibModule;begin  if FSystemModulesOnly then  begin    CurModule := LibModuleList;    while CurModule <> nil do    begin      CreateItemForAddress(Pointer(CurModule.Instance), True);      CurModule := CurModule.Next;    end;  end  else  begin    List := TStringList.Create;    try      LoadedModulesList(List, GetCurrentProcessId, True);      for I := 0 to List.Count - 1 do        CreateItemForAddress(List.Objects[I], False);    finally      List.Free;    end;  end;  //Sort(SortByStartAddress);end;function TJclModuleInfoList.CreateItemForAddress(Addr: Pointer; SystemModule: Boolean): TJclModuleInfo;var  Module: HMODULE;  ModuleSize: DWORD;begin  Result := nil;  Module := ModuleFromAddr(Addr);  if Module > 0 then  begin    ModuleSize := PeMapImgSize(Pointer(Module));    if ModuleSize <> 0 then    begin      Result := TJclModuleInfo.Create;      Result.FStartAddr := Pointer(Module);      Result.FSize := ModuleSize;      Result.FEndAddr := Pointer(Module + ModuleSize - 1);      if SystemModule then        Result.FSystemModule := True      else        Result.FSystemModule := IsSystemModule(Module);    end;  end;  if Result <> nil then    Add(Result);end;function TJclModuleInfoList.GetItems(Index: TJclListSize): TJclModuleInfo;begin  Result := TJclModuleInfo(Get(Index));end;function TJclModuleInfoList.GetModuleFromAddress(Addr: Pointer): TJclModuleInfo;var  I: Integer;  Item: TJclModuleInfo;begin  Result := nil;  for I := 0 to Count - 1 do  begin    Item := Items[I];    if (TJclAddr(Item.StartAddr) <= TJclAddr(Addr)) and (TJclAddr(Item.EndAddr) > TJclAddr(Addr)) then    begin      Result := Item;      Break;    end;  end;  if DynamicBuild and (Result = nil) then    Result := CreateItemForAddress(Addr, False);end;function TJclModuleInfoList.IsSystemModuleAddress(Addr: Pointer): Boolean;var  Item: TJclModuleInfo;begin  Item := ModuleFromAddress[Addr];  Result := (Item <> nil) and Item.SystemModule;end;function TJclModuleInfoList.IsValidModuleAddress(Addr: Pointer): Boolean;begin  Result := ModuleFromAddress[Addr] <> nil;end;//=== { TJclAbstractMapParser } ==============================================constructor TJclAbstractMapParser.Create(const MapFileName: TFileName; Module: HMODULE);begin  inherited Create;  FModule := Module;  if FileExists(MapFileName) then    FStream := TJclFileMappingStream.Create(MapFileName, fmOpenRead or fmShareDenyWrite);end;constructor TJclAbstractMapParser.Create(const MapFileName: TFileName);begin  Create(MapFileName, 0);end;destructor TJclAbstractMapParser.Destroy;begin  FreeAndNil(FStream);  inherited Destroy;end;function TJclAbstractMapParser.GetLinkerBugUnitName: string;begin  Result := MapStringToStr(FLinkerBugUnitName);end;class function TJclAbstractMapParser.MapStringToFileName(MapString: PJclMapString): string;var  PEnd: PJclMapString;begin  if MapString = nil then  begin    Result := '';    Exit;  end;  PEnd := MapString;  while (PEnd^ <> #0) and not (PEnd^ in ['=', #10, #13]) do    Inc(PEnd);  if (PEnd^ = '=') then  begin    while (PEnd >= MapString) and (PEnd^ <> ' ') do      Dec(PEnd);    while (PEnd >= MapString) and ((PEnd-1)^ = ' ') do      Dec(PEnd);  end;  SetString(Result, MapString, PEnd - MapString);end;class function TJclAbstractMapParser.MapStringToModuleName(MapString: PJclMapString): string;var  PStart, PEnd, PExtension: PJclMapString;begin  if MapString = nil then  begin    Result := '';    Exit;  end;  PEnd := MapString;  while (PEnd^ <> #0) and not (PEnd^ in ['=', #10, #13]) do    Inc(PEnd);  if (PEnd^ = '=') then  begin    while (PEnd >= MapString) and (PEnd^ <> ' ') do      Dec(PEnd);    while (PEnd >= MapString) and ((PEnd-1)^ = ' ') do      Dec(PEnd);  end;  PExtension := PEnd;  while (PExtension >= MapString) and (PExtension^ <> '.') and (PExtension^ <> '|') do    Dec(PExtension);  if (StrLICompA(PExtension, '.pas ', 5) = 0) or     (StrLICompA(PExtension, '.obj ', 5) = 0) then    PEnd := PExtension;  PExtension := PEnd;  while (PExtension >= MapString) and (PExtension^ <> '|') and (PExtension^ <> '\') do    Dec(PExtension);  if PExtension >= MapString then    PStart := PExtension + 1  else    PStart := MapString;  SetString(Result, PStart, PEnd - PStart);end;class function TJclAbstractMapParser.MapStringToStr(MapString: PJclMapString;  IgnoreSpaces: Boolean): string;var  P: PJclMapString;begin  if MapString = nil then  begin    Result := '';    Exit;  end;  if MapString^ = '(' then  begin    Inc(MapString);    P := MapString;    while (P^ <> #0) and not (P^ in [')', #10, #13]) do      Inc(P);  end  else  begin    P := MapString;    if IgnoreSpaces then      while (P^ <> #0) and not (P^ in ['(', #10, #13]) do        Inc(P)    else      while (P^ <> #0) and (P^ <> '(') and (P^ > ' ') do        Inc(P);  end;  SetString(Result, MapString, P - MapString);end;function IsDecDigit(P: PJclMapString): Boolean; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}begin  Result := False;  case P^ of    '0'..'9':      Result := True;  end;end;function SkipMapBlock(P, EndPos: PJclMapString): PJclMapString;begin  Result := P;  while Result < EndPos do  begin    if not IsDecDigit(Result) then      Break;    Inc(Result);    // Skip to the end of the line    while Result < EndPos do    begin      case Result^ of        #10, #13:          Break;      end;      Inc(Result);    end;    // Skip WhiteSpaces    while (Result < EndPos) and (Result^ <= ' ') do      Inc(Result);  end;end;function AnsiStrPosIdxLen(const SubStr, S: PAnsiChar; Len: Integer): Integer;var  I: Integer;  SubStrLen: Integer;  FirstCh: AnsiChar;begin  Result := 0;  if Len = 0 then    Exit;  I := 0;  FirstCh := SubStr[0];  if FirstCh = #0 then    Exit;  SubStrLen := StrLenA(SubStr);  while I < Len do  begin    while (I < Len) and (S[I] <> FirstCh) do      Inc(I);    if I = Len then      Break;    if StrLCompA(SubStr, @S[I], SubStrLen) = 0 then    begin      Result := I + 1;      Exit;    end;    Inc(I);  end;end;procedure TJclAbstractMapParser.Parse;const  TableHeader          : array [0..3] of PJclMapString = ('Start', 'Length', 'Name', 'Class');  SegmentsHeader       : array [0..3] of PJclMapString = ('Detailed', 'map', 'of', 'segments');  PublicsByNameHeader  : array [0..3] of PJclMapString = ('Address', 'Publics', 'by', 'Name');  PublicsByValueHeader : array [0..3] of PJclMapString = ('Address', 'Publics', 'by', 'Value');  LineNumbersPrefix    : PJclMapString = 'Line numbers for';var  CurrPos, EndPos: PJclMapString;{$IFNDEF COMPILER9_UP}  PreviousA,{$ENDIF COMPILER9_UP}  A: TJclMapAddress;  L: Integer;  P1, P2: PJclMapString;  function Eof: Boolean;  begin    Result := CurrPos >= EndPos;  end;  function SkipWhiteSpace: PJclMapString;  var    LEndPos: PJclMapString;  begin    Result := CurrPos;    LEndPos := EndPos;    while (Result < LEndPos) and (Result^ <= ' ') do      Inc(Result);    CurrPos := Result;  end;  procedure SkipEndLine;  var    P, LEndPos: PJclMapString;  begin    P := CurrPos;    LEndPos := EndPos;    while P < LEndPos do    begin      case P^ of        #10, #13:          Break;      end;      Inc(P);    end;    // Skip WhiteSpaces    while (P < LEndPos) and (P^ <= ' ') do      Inc(P);    CurrPos := P;  end;  function ReadTrimmedTextLine(var Len: Integer): PJclMapString;  var    Start, P: PJclMapString;  begin    Start := CurrPos;    P := Start;    while (P^ <> #0) and not (P^ in [#10, #13]) do      Inc(P);    CurrPos := P;    // Trim    while (Start < P) and (Start^ <> #0) and (Start^ <= ' ') do      Inc(Start);    Dec(P);    while (P > Start) and (P^ <= ' ') do      Dec(P);    Inc(P);    Result := Start;    Len := P - Start;    if Len < 0 then      Len := 0;  end;  function ReadDecValue: Integer;  var    P: PJclMapString;  begin    P := CurrPos;    Result := 0;    while P^ in ['0'..'9'] do    begin      Result := Result * 10 + (Ord(P^) - Ord('0'));      Inc(P);    end;    CurrPos := P;  end;  function ReadHexValue: DWORD;  var    C: AnsiChar;    V: Byte;    P: PJclMapString;  begin    P := CurrPos;    Result := 0;    repeat      C := P^;      V := HexMap[C];      if V and $80 <> 0 then        Break;      Result := (Result shl 4) or V;      Inc(P);    until False;    if (C = 'H') or (C = 'h') then      Inc(P);    CurrPos := P;  end;  procedure ReadAddress(var Result: TJclMapAddress);  begin    Result.Segment := ReadHexValue;    if CurrPos^ = ':' then    begin      Inc(CurrPos);      Result.Offset := ReadHexValue;    end    else      Result.Offset := 0;  end;  function ReadString: PJclMapString;  var    P, LEndPos: PJclMapString;  begin    // Skip WhiteSpaces    LEndPos := EndPos;    P := CurrPos;    while (P < LEndPos) and (P^ <= ' ') do      Inc(P);    Result := P;    while {(P^ <> #0) and} (P^ > ' ') do      Inc(P);    CurrPos := P;  end;  procedure FindParam(Param: AnsiChar);  var    P: PJclMapString;  begin    P := CurrPos;    while not ((P^ = Param) and (P[1] = '=')) do      Inc(P);    CurrPos := P + 2;  end;  function SyncToHeader(const Header: array of PJclMapString): Boolean;  var    S: PJclMapString;    SLen: Integer;    TokenIndex, OldPosition, CurrentPosition: Integer;  begin    Result := False;    while not Eof do    begin      S := ReadTrimmedTextLine(SLen);      if SLen > 0 then      begin        TokenIndex := Low(Header);        CurrentPosition := 0;        OldPosition := 0;        while (TokenIndex <= High(Header)) do        begin          CurrentPosition := AnsiStrPosIdxLen(Header[TokenIndex], S, SLen);          if (CurrentPosition <= OldPosition) then          begin            CurrentPosition := 0;            Break;          end;          OldPosition := CurrentPosition;          Inc(TokenIndex);        end;        Result := CurrentPosition <> 0;        if Result then          Break;      end;      SkipEndLine;    end;    if not Eof then      SkipWhiteSpace;  end;  function SyncToPrefix(const Prefix: PJclMapString): Boolean;  var    P: PJclMapString;    PrefixLen: Integer;  begin    if Eof then    begin      Result := False;      Exit;    end;    SkipWhiteSpace;    P := CurrPos;    PrefixLen := StrLenA(Prefix);    Result := StrLCompA(Prefix, P, PrefixLen) = 0;    if Result then      CurrPos := P + PrefixLen;    SkipWhiteSpace;  end;begin  if FStream <> nil then  begin    FLinkerBug := False;{$IFNDEF COMPILER9_UP}    PreviousA.Segment := 0;    PreviousA.Offset := 0;{$ENDIF COMPILER9_UP}    CurrPos := FStream.Memory;    EndPos := CurrPos + FStream.Size;    if SyncToHeader(TableHeader) then      while IsDecDigit(CurrPos) do      begin        ReadAddress(A);        SkipWhiteSpace;        L := ReadHexValue;        P1 := ReadString;        P2 := ReadString;        SkipEndLine;        ClassTableItem(A, L, P1, P2);      end;    if SyncToHeader(SegmentsHeader) then      while IsDecDigit(CurrPos) do      begin        ReadAddress(A);        SkipWhiteSpace;        L := ReadHexValue;        FindParam('C');        P1 := ReadString;        FindParam('M');        P2 := ReadString;        SkipEndLine;        SegmentItem(A, L, P1, P2);      end;    if SyncToHeader(PublicsByNameHeader) then    begin      if not CanHandlePublicsByName then        CurrPos := SkipMapBlock(CurrPos, EndPos)      else      begin        while IsDecDigit(CurrPos) do        begin          ReadAddress(A);          P1 := ReadString;          SkipEndLine; // compatibility with C++Builder MAP files          PublicsByNameItem(A, P1);        end;      end;    end;    if SyncToHeader(PublicsByValueHeader) then      if not CanHandlePublicsByValue then        CurrPos := SkipMapBlock(CurrPos, EndPos)      else      begin        while not Eof and IsDecDigit(CurrPos) do        begin          ReadAddress(A);          P1 := ReadString;          SkipEndLine; // compatibility with C++Builder MAP files          PublicsByValueItem(A, P1);        end;      end;    while SyncToPrefix(LineNumbersPrefix) do    begin      FLastUnitName := CurrPos;      FLastUnitFileName := CurrPos;      while FLastUnitFileName^ <> '(' do        Inc(FLastUnitFileName);      SkipEndLine;      LineNumberUnitItem(FLastUnitName, FLastUnitFileName);      repeat        SkipWhiteSpace;        L := ReadDecValue;        SkipWhiteSpace;        ReadAddress(A);        SkipWhiteSpace;        LineNumbersItem(L, A);{$IFNDEF COMPILER9_UP}        if not FLinkerBug and (A.Offset < PreviousA.Offset) then        begin          FLinkerBugUnitName := FLastUnitName;          FLinkerBug := True;        end;        PreviousA := A;{$ENDIF COMPILER9_UP}      until not IsDecDigit(CurrPos);    end;  end;end;//=== { TJclMapParser } ======================================================procedure TJclMapParser.ClassTableItem(const Address: TJclMapAddress;  Len: Integer; SectionName, GroupName: PJclMapString);begin  if Assigned(FOnClassTable) then    FOnClassTable(Self, Address, Len, MapStringToStr(SectionName), MapStringToStr(GroupName));end;procedure TJclMapParser.LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress);begin  if Assigned(FOnLineNumbers) then    FOnLineNumbers(Self, LineNumber, Address);end;procedure TJclMapParser.LineNumberUnitItem(UnitName, UnitFileName: PJclMapString);begin  if Assigned(FOnLineNumberUnit) then    FOnLineNumberUnit(Self, MapStringToStr(UnitName), MapStringToStr(UnitFileName));end;function TJclMapParser.CanHandlePublicsByName: Boolean;begin  Result := Assigned(FOnPublicsByName);end;function TJclMapParser.CanHandlePublicsByValue: Boolean;begin  Result := Assigned(FOnPublicsByValue);end;procedure TJclMapParser.PublicsByNameItem(const Address: TJclMapAddress;  Name: PJclMapString);begin  if Assigned(FOnPublicsByName) then    // MAP files generated by C++Builder have spaces in their identifier names    FOnPublicsByName(Self, Address, MapStringToStr(Name, True));end;procedure TJclMapParser.PublicsByValueItem(const Address: TJclMapAddress;  Name: PJclMapString);begin  if Assigned(FOnPublicsByValue) then    // MAP files generated by C++Builder have spaces in their identifier names    FOnPublicsByValue(Self, Address, MapStringToStr(Name, True));end;procedure TJclMapParser.SegmentItem(const Address: TJclMapAddress;  Len: Integer; GroupName, UnitName: PJclMapString);begin  if Assigned(FOnSegmentItem) then    FOnSegmentItem(Self, Address, Len, MapStringToStr(GroupName), MapStringToModuleName(UnitName));end;//=== { TJclMapScanner } =====================================================constructor TJclMapScanner.Create(const MapFileName: TFileName; Module: HMODULE);begin  inherited Create(MapFileName, Module);  Scan;end;function TJclMapScanner.MAPAddrToVA(const Addr: DWORD): DWORD;begin  // MAP file format was changed in Delphi 2005  // before Delphi 2005: segments started at offset 0  //                     only one segment of code  // after Delphi 2005: segments started at code base address (module base address + $10000)  //                    2 segments of code  if (Length(FSegmentClasses) > 0) and (FSegmentClasses[0].Start > 0) and (Addr >= FSegmentClasses[0].Start) then    // Delphi 2005 and later    // The first segment should be code starting at module base address + $10000    Result := Addr - FSegmentClasses[0].Start  else    // before Delphi 2005    Result := Addr;end;class function TJclMapScanner.MapStringCacheToFileName(  var MapString: TJclMapStringCache): string;begin  Result := MapString.CachedValue;  if Result = '' then  begin    Result := MapStringToFileName(MapString.RawValue);    MapString.CachedValue := Result;  end;end;class function TJclMapScanner.MapStringCacheToModuleName(  var MapString: TJclMapStringCache): string;begin  Result := MapString.CachedValue;  if Result = '' then  begin    Result := MapStringToModuleName(MapString.RawValue);    MapString.CachedValue := Result;  end;end;class function TJclMapScanner.MapStringCacheToStr(var MapString: TJclMapStringCache;  IgnoreSpaces: Boolean): string;begin  Result := MapString.CachedValue;  if Result = '' then  begin    Result := MapStringToStr(MapString.RawValue, IgnoreSpaces);    MapString.CachedValue := Result;  end;end;procedure TJclMapScanner.ClassTableItem(const Address: TJclMapAddress; Len: Integer;  SectionName, GroupName: PJclMapString);var  C: Integer;  SectionHeader: PImageSectionHeader;begin  C := Length(FSegmentClasses);  SetLength(FSegmentClasses, C + 1);  FSegmentClasses[C].Segment := Address.Segment;  FSegmentClasses[C].Start := Address.Offset;  FSegmentClasses[C].Addr := Address.Offset; // will be fixed below while considering module mapped address  // test GroupName because SectionName = '.tls' in Delphi and '_tls' in BCB  if StrLICompA(GroupName, 'TLS', 3) = 0 then  begin    FSegmentClasses[C].VA := FSegmentClasses[C].Start;    FSegmentClasses[C].GroupName.TLS := True;  end  else  begin    FSegmentClasses[C].VA := MAPAddrToVA(FSegmentClasses[C].Start);    FSegmentClasses[C].GroupName.TLS := False;  end;  FSegmentClasses[C].Len := Len;  FSegmentClasses[C].SectionName.RawValue := SectionName;  FSegmentClasses[C].GroupName.RawValue := GroupName;  if FModule <> 0 then  begin    { Fix the section addresses }    SectionHeader := PeMapImgFindSectionFromModule(Pointer(FModule), MapStringToStr(SectionName));    if SectionHeader = nil then      { before Delphi 2005 the class names where used for the section names }      SectionHeader := PeMapImgFindSectionFromModule(Pointer(FModule), MapStringToStr(GroupName));    if SectionHeader <> nil then    begin      FSegmentClasses[C].Addr := TJclAddr(FModule) + SectionHeader.VirtualAddress;      FSegmentClasses[C].VA := SectionHeader.VirtualAddress;    end;  end;end;function TJclMapScanner.LineNumberFromAddr(Addr: DWORD): Integer;var  Dummy: Integer;begin  Result := LineNumberFromAddr(Addr, Dummy);end;function Search_MapLineNumber(Item1, Item2: Pointer): Integer;begin  Result := Integer(PJclMapLineNumber(Item1)^.VA) - PInteger(Item2)^;end;function TJclMapScanner.LineNumberFromAddr(Addr: DWORD; out Offset: Integer): Integer;var  I: Integer;  ModuleStartAddr: DWORD;begin  ModuleStartAddr := ModuleStartFromAddr(Addr);  Result := 0;  Offset := 0;  I := SearchDynArray(FLineNumbers, SizeOf(FLineNumbers[0]), Search_MapLineNumber, @Addr, True);  if (I <> -1) and (FLineNumbers[I].VA >= ModuleStartAddr) then  begin    Result := FLineNumbers[I].LineNumber;    Offset := Addr - FLineNumbers[I].VA;  end;end;procedure TJclMapScanner.LineNumbersItem(LineNumber: Integer; const Address: TJclMapAddress);var  SegIndex, C: Integer;  VA: DWORD;  Added: Boolean;begin  Added := False;  for SegIndex := Low(FSegmentClasses) to High(FSegmentClasses) do    if (FSegmentClasses[SegIndex].Segment = Address.Segment)      and (DWORD(Address.Offset) < FSegmentClasses[SegIndex].Len) then  begin    if FSegmentClasses[SegIndex].GroupName.TLS then      Va := Address.Offset    else      VA := MAPAddrToVA(Address.Offset + FSegmentClasses[SegIndex].Start);    { Starting with Delphi 2005, "empty" units are listes with the last line and      the VA 0001:00000000. When we would accept 0 VAs here, System.pas functions      could be mapped to other units and line numbers. Discaring such items should      have no impact on the correct information, because there can't be a function      that starts at VA 0. }    if VA = 0 then      Continue;    if FLineNumbersCnt = Length(FLineNumbers)  then    begin      if FLineNumbersCnt < 512 then        SetLength(FLineNumbers, FLineNumbersCnt + 512)      else        SetLength(FLineNumbers, FLineNumbersCnt * 2);    end;    FLineNumbers[FLineNumbersCnt].Segment := FSegmentClasses[SegIndex].Segment;    FLineNumbers[FLineNumbersCnt].VA := VA;    FLineNumbers[FLineNumbersCnt].LineNumber := LineNumber;    FLineNumbers[FLineNumbersCnt].UnitName := FCurrentUnitName;    Inc(FLineNumbersCnt);    Added := True;    if FNewUnitFileName <> nil then    begin      C := Length(FSourceNames);      SetLength(FSourceNames, C + 1);      FSourceNames[C].Segment := FSegmentClasses[SegIndex].Segment;      FSourceNames[C].VA := VA;      FSourceNames[C].ProcName.RawValue := FNewUnitFileName;      FNewUnitFileName := nil;    end;    Break;  end;  if not Added then    Inc(FLineNumberErrors);end;procedure TJclMapScanner.LineNumberUnitItem(UnitName, UnitFileName: PJclMapString);begin  FNewUnitFileName := UnitFileName;  FCurrentUnitName := UnitName;end;function TJclMapScanner.GetLineNumberByIndex(Index: Integer): TJCLMapLineNumber;begin  Result := FLineNumbers[Index];end;function TJclMapScanner.IndexOfSegment(Addr: DWORD): Integer;var  L, R: Integer;  S: PJclMapSegment;begin  R := Length(FSegments) - 1;  Result := FLastAccessedSegementIndex;  if Result <= R then  begin    S := @FSegments[Result];    if (S.StartVA <= Addr) and (Addr < S.EndVA) then      Exit;  end;  // binary search  L := 0;  while L <= R do  begin    Result := L + (R - L) div 2;    S := @FSegments[Result];    if Addr >= S.EndVA then      L := Result + 1    else    begin      R := Result - 1;      if (S.StartVA <= Addr) and (Addr < S.EndVA) then      begin        FLastAccessedSegementIndex := Result;        Exit;      end;    end;  end;  Result := -1;end;function TJclMapScanner.ModuleNameFromAddr(Addr: DWORD): string;var  I: Integer;begin  I := IndexOfSegment(Addr);  if I <> -1 then    Result := MapStringCacheToModuleName(FSegments[I].UnitName)  else    Result := '';end;function TJclMapScanner.ModuleStartFromAddr(Addr: DWORD): DWORD;var  I: Integer;begin  I := IndexOfSegment(Addr);  Result := DWORD(-1);  if I <> -1 then    Result := FSegments[I].StartVA;end;function TJclMapScanner.ProcNameFromAddr(Addr: DWORD): string;var  Dummy: Integer;begin  Result := ProcNameFromAddr(Addr, Dummy);end;function Search_MapProcName(Item1, Item2: Pointer): Integer;begin  Result := Integer(PJclMapProcName(Item1)^.VA) - PInteger(Item2)^;end;function TJclMapScanner.ProcNameFromAddr(Addr: DWORD; out Offset: Integer): string;var  I: Integer;  ModuleStartAddr: DWORD;begin  ModuleStartAddr := ModuleStartFromAddr(Addr);  Result := '';  Offset := 0;  I := SearchDynArray(FProcNames, SizeOf(FProcNames[0]), Search_MapProcName, @Addr, True);  if (I <> -1) and (FProcNames[I].VA >= ModuleStartAddr) then  begin    Result := MapStringCacheToStr(FProcNames[I].ProcName, True);    Offset := Addr - FProcNames[I].VA;  end;end;function TJclMapScanner.CanHandlePublicsByName: Boolean;begin  Result := False;end;function TJclMapScanner.CanHandlePublicsByValue: Boolean;begin  Result := True;end;procedure TJclMapScanner.PublicsByNameItem(const Address: TJclMapAddress;  Name: PJclMapString);beginend;procedure TJclMapScanner.PublicsByValueItem(const Address: TJclMapAddress; Name: PJclMapString);var  SegIndex: Integer;begin  for SegIndex := Low(FSegmentClasses) to High(FSegmentClasses) do    if (FSegmentClasses[SegIndex].Segment = Address.Segment)      and (DWORD(Address.Offset) < FSegmentClasses[SegIndex].Len) then  begin    if FProcNamesCnt = Length(FProcNames)  then    begin      if FProcNamesCnt < 512 then        SetLength(FProcNames, FProcNamesCnt + 512)      else        SetLength(FProcNames, FProcNamesCnt * 2);    end;    FProcNames[FProcNamesCnt].Segment := FSegmentClasses[SegIndex].Segment;    if FSegmentClasses[SegIndex].GroupName.TLS then      FProcNames[FProcNamesCnt].VA := Address.Offset    else      FProcNames[FProcNamesCnt].VA := MAPAddrToVA(Address.Offset + FSegmentClasses[SegIndex].Start);    FProcNames[FProcNamesCnt].ProcName.RawValue := Name;    Inc(FProcNamesCnt);    Break;  end;end;{function Sort_MapLineNumber(Item1, Item2: Pointer): Integer;begin  Result := Integer(PJclMapLineNumber(Item1)^.VA) - Integer(PJclMapLineNumber(Item2)^.VA);end;}function Sort_MapProcName(Item1, Item2: Pointer): Integer;begin  Result := Integer(PJclMapProcName(Item1)^.VA) - Integer(PJclMapProcName(Item2)^.VA);end;function Sort_MapSegment(Item1, Item2: Pointer): Integer;begin  Result := Integer(PJclMapSegment(Item1)^.EndVA) - Integer(PJclMapSegment(Item2)^.EndVA);  if Result = 0 then    Result := Integer(PJclMapSegment(Item1)^.StartVA) - Integer(PJclMapSegment(Item2)^.StartVA);end;type  PJclMapLineNumberArray = ^TJclMapLineNumberArray;  TJclMapLineNumberArray = array[0..MaxInt div SizeOf(TJclMapLineNumber) - 1] of TJclMapLineNumber;  PJclMapProcNameArray = ^TJclMapProcNameArray;  TJclMapProcNameArray = array[0..MaxInt div SizeOf(TJclMapProcName) - 1] of TJclMapProcName;// specialized quicksort functionsprocedure SortLineNumbers(ArrayVar: PJclMapLineNumberArray; L, R: Integer);var  I, J, P: Integer;  Temp: TJclMapLineNumber;  AV: PJclMapLineNumber;  V: Integer;begin  repeat    I := L;    J := R;    P := (L + R) shr 1;    repeat      V := Integer(ArrayVar[P].VA);      AV := @ArrayVar[I];      while Integer(AV.VA) - V < 0 do begin Inc(I); Inc(AV); end;      AV := @ArrayVar[J];      while Integer(AV.VA) - V > 0 do begin Dec(J); Dec(AV); end;      if I <= J then      begin        if I <> J then        begin          Temp := ArrayVar[I];          ArrayVar[I] := ArrayVar[J];          ArrayVar[J] := Temp;        end;        if P = I then          P := J        else if P = J then          P := I;        Inc(I);        Dec(J);      end;    until I > J;    if L < J then      SortLineNumbers(ArrayVar, L, J);    L := I;  until I >= R;end;procedure SortProcNames(ArrayVar: PJclMapProcNameArray; L, R: Integer);var  I, J, P: Integer;  Temp: TJclMapProcName;  V: Integer;  AV: PJclMapProcName;begin  repeat    I := L;    J := R;    P := (L + R) shr 1;    repeat      V := Integer(ArrayVar[P].VA);      AV := @ArrayVar[I];      while Integer(AV.VA) - V < 0 do begin Inc(I); Inc(AV); end;      AV := @ArrayVar[J];      while Integer(AV.VA) - V > 0 do begin Dec(J); Dec(AV); end;      if I <= J then      begin        if I <> J then        begin          Temp := ArrayVar[I];          ArrayVar[I] := ArrayVar[J];          ArrayVar[J] := Temp;        end;        if P = I then          P := J        else if P = J then          P := I;        Inc(I);        Dec(J);      end;    until I > J;    if L < J then      SortProcNames(ArrayVar, L, J);    L := I;  until I >= R;end;procedure TJclMapScanner.Scan;begin  FLineNumberErrors := 0;  FSegmentCnt := 0;  FProcNamesCnt := 0;  FLastAccessedSegementIndex := 0;  Parse;  SetLength(FLineNumbers, FLineNumbersCnt);  SetLength(FProcNames, FProcNamesCnt);  SetLength(FSegments, FSegmentCnt);  //SortDynArray(FLineNumbers, SizeOf(FLineNumbers[0]), Sort_MapLineNumber);  if FLineNumbers <> nil then    SortLineNumbers(PJclMapLineNumberArray(FLineNumbers), 0, Length(FLineNumbers) - 1);  //SortDynArray(FProcNames, SizeOf(FProcNames[0]), Sort_MapProcName);  if FProcNames <> nil then    SortProcNames(PJclMapProcNameArray(FProcNames), 0, Length(FProcNames) - 1);  SortDynArray(FSegments, SizeOf(FSegments[0]), Sort_MapSegment);  SortDynArray(FSourceNames, SizeOf(FSourceNames[0]), Sort_MapProcName);end;procedure TJclMapScanner.SegmentItem(const Address: TJclMapAddress; Len: Integer;  GroupName, UnitName: PJclMapString);var  SegIndex: Integer;  VA: DWORD;begin  for SegIndex := Low(FSegmentClasses) to High(FSegmentClasses) do    if (FSegmentClasses[SegIndex].Segment = Address.Segment)      and (DWORD(Address.Offset) < FSegmentClasses[SegIndex].Len) then  begin    if FSegmentClasses[SegIndex].GroupName.TLS then      VA := Address.Offset    else      VA := MAPAddrToVA(Address.Offset + FSegmentClasses[SegIndex].Start);    if FSegmentCnt mod 16 = 0 then      SetLength(FSegments, FSegmentCnt + 16);    FSegments[FSegmentCnt].Segment := FSegmentClasses[SegIndex].Segment;    FSegments[FSegmentCnt].StartVA := VA;    FSegments[FSegmentCnt].EndVA := VA + DWORD(Len);    FSegments[FSegmentCnt].UnitName.RawValue := UnitName;    Inc(FSegmentCnt);    Break;  end;end;function TJclMapScanner.SourceNameFromAddr(Addr: DWORD): string;var  I: Integer;  ModuleStartVA: DWORD;begin  // try with line numbers first (Delphi compliance)  ModuleStartVA := ModuleStartFromAddr(Addr);  Result := '';  I := SearchDynArray(FSourceNames, SizeOf(FSourceNames[0]), Search_MapProcName, @Addr, True);  if (I <> -1) and (FSourceNames[I].VA >= ModuleStartVA) then    Result := MapStringCacheToStr(FSourceNames[I].ProcName);  if Result = '' then  begin    // try with module names (C++Builder compliance)    I := IndexOfSegment(Addr);    if I <> -1 then      Result := MapStringCacheToFileName(FSegments[I].UnitName);  end;end;function TJclMapScanner.VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;var  I: Integer;  QualifiedName: string;begin  Result := 0;  if (UnitName = '') or (ProcName = '') then    Exit;  QualifiedName := UnitName + '.' + ProcName;  for I := Low(FProcNames) to High(FProcNames) do  begin    if CompareText(MapStringCacheToStr(FProcNames[I].ProcName, True), QualifiedName) = 0 then    begin      Result := FProcNames[i].VA;      Break;    end;  end;end;// JCL binary debug format string encoding/decoding routines{ Strings are compressed to following 6bit format (A..D represents characters) and terminated with }{ 6bit #0 char. First char = #1 indicates non compressed text, #2 indicates compressed text with   }{ leading '@' character                                                                            }{                                                                                                  }{ 7   6   5   4   3   2   1   0  |                                                                 }{---------------------------------                                                                 }{ B1  B0  A5  A4  A3  A2  A1  A0 | Data byte 0                                                     }{---------------------------------                                                                 }{ C3  C2  C1  C0  B5  B4  B3  B2 | Data byte 1                                                     }{---------------------------------                                                                 }{ D5  D4  D3  D2  D1  D0  C5  C4 | Data byte 2                                                     }{---------------------------------                                                                 }function SimpleCryptString(const S: TUTF8String): TUTF8String;var  I: Integer;  C: Byte;  P: PByte;begin  SetLength(Result, Length(S));  P := PByte(Result);  for I := 1 to Length(S) do  begin    C := Ord(S[I]);    if C <> $AA then      C := C xor $AA;    P^ := C;    Inc(P);  end;end;function DecodeNameString(const S: PAnsiChar): string;var  I, B: Integer;  C: Byte;  P: PByte;  Buffer: array [0..255] of AnsiChar;begin  Result := '';  B := 0;  P := PByte(S);  case P^ of    1:      begin        Inc(P);        Result := UTF8ToString(SimpleCryptString(PAnsiChar(P)));        Exit;      end;    2:      begin        Inc(P);        Buffer[B] := '@';        Inc(B);      end;  end;  I := 0;  C := 0;  repeat    case I and $03 of      0:        C := P^ and $3F;      1:        begin          C := (P^ shr 6) and $03;          Inc(P);          Inc(C, (P^ and $0F) shl 2);        end;      2:        begin          C := (P^ shr 4) and $0F;          Inc(P);          Inc(C, (P^ and $03) shl 4);        end;      3:        begin          C := (P^ shr 2) and $3F;          Inc(P);        end;    end;    case C of      $00:        Break;      $01..$0A:        Inc(C, Ord('0') - $01);      $0B..$24:        Inc(C, Ord('A') - $0B);      $25..$3E:        Inc(C, Ord('a') - $25);      $3F:        C := Ord('_');    end;    Buffer[B] := AnsiChar(C);    Inc(B);    Inc(I);  until B >= SizeOf(Buffer) - 1;  Buffer[B] := #0;  Result := UTF8ToString(Buffer);end;function EncodeNameString(const S: string): AnsiString;var  I, StartIndex, EndIndex: Integer;  C: Byte;  P: PByte;begin  if (Length(S) > 1) and (S[1] = '@') then    StartIndex := 1  else    StartIndex := 0;  for I := StartIndex + 1 to Length(S) do    if not CharIsValidIdentifierLetter(Char(S[I])) then    begin      {$IFDEF SUPPORTS_UNICODE}      Result := #1 + SimpleCryptString(UTF8Encode(S)) + #0; // UTF8Encode is much faster than StringToUTF8      {$ELSE}      Result := #1 + SimpleCryptString(StringToUTF8(S)) + #0;      {$ENDIF SUPPORTS_UNICODE}      Exit;    end;  SetLength(Result, Length(S) + StartIndex);  P := Pointer(Result);  if StartIndex = 1 then    P^ := 2 // store '@' leading char information  else    Dec(P);  EndIndex := Length(S) - StartIndex;  for I := 0 to EndIndex do // including null char  begin    if I = EndIndex then      C := 0    else      C := Byte(S[I + 1 + StartIndex]);    case AnsiChar(C) of      #0:        C := 0;      '0'..'9':        Dec(C, Ord('0') - $01);      'A'..'Z':        Dec(C, Ord('A') - $0B);      'a'..'z':        Dec(C, Ord('a') - $25);      '_':        C := $3F;    else      C := $3F;    end;    case I and $03 of      0:        begin          Inc(P);          P^ := C;        end;      1:        begin          P^ := P^ or (C and $03) shl 6;          Inc(P);          P^ := (C shr 2) and $0F;        end;      2:        begin          P^ := P^ or Byte(C shl 4);          Inc(P);          P^ := (C shr 4) and $03;        end;      3:        P^ := P^ or (C shl 2);    end;  end;  SetLength(Result, TJclAddr(P) - TJclAddr(Pointer(Result)) + 1);end;function ConvertMapFileToJdbgFile(const MapFileName: TFileName): Boolean;var  Dummy1: string;  Dummy2, Dummy3, Dummy4: Integer;begin  Result := ConvertMapFileToJdbgFile(MapFileName, Dummy1, Dummy2, Dummy3, Dummy4);end;function ConvertMapFileToJdbgFile(const MapFileName: TFileName; out LinkerBugUnit: string;  out LineNumberErrors: Integer): Boolean;var  Dummy1, Dummy2: Integer;begin  Result := ConvertMapFileToJdbgFile(MapFileName, LinkerBugUnit, LineNumberErrors,    Dummy1, Dummy2);end;function ConvertMapFileToJdbgFile(const MapFileName: TFileName; out LinkerBugUnit: string;  out LineNumberErrors, MapFileSize, JdbgFileSize: Integer): Boolean;var  JDbgFileName: TFileName;  Generator: TJclBinDebugGenerator;begin  JDbgFileName := ChangeFileExt(MapFileName, JclDbgFileExtension);  Generator := TJclBinDebugGenerator.Create(MapFileName, 0);  try    MapFileSize := Generator.Stream.Size;    JdbgFileSize := Generator.DataStream.Size;    Result := (Generator.DataStream.Size > 0) and Generator.CalculateCheckSum;    if Result then      Generator.DataStream.SaveToFile(JDbgFileName);    LinkerBugUnit := Generator.LinkerBugUnitName;    LineNumberErrors := Generator.LineNumberErrors;  finally    Generator.Free;  end;end;function InsertDebugDataIntoExecutableFile(const ExecutableFileName, MapFileName: TFileName;  out LinkerBugUnit: string; out MapFileSize, JclDebugDataSize: Integer): Boolean;var  Dummy: Integer;begin  Result := InsertDebugDataIntoExecutableFile(ExecutableFileName, MapFileName, LinkerBugUnit,    MapFileSize, JclDebugDataSize, Dummy);end;function InsertDebugDataIntoExecutableFile(const ExecutableFileName, MapFileName: TFileName;  out LinkerBugUnit: string; out MapFileSize, JclDebugDataSize, LineNumberErrors: Integer): Boolean;var  BinDebug: TJclBinDebugGenerator;begin  BinDebug := TJclBinDebugGenerator.Create(MapFileName, 0);  try    Result := InsertDebugDataIntoExecutableFile(ExecutableFileName, BinDebug,      LinkerBugUnit, MapFileSize, JclDebugDataSize, LineNumberErrors);  finally    BinDebug.Free;  end;end;function InsertDebugDataIntoExecutableFile(const ExecutableFileName: TFileName;  BinDebug: TJclBinDebugGenerator; out LinkerBugUnit: string;  out MapFileSize, JclDebugDataSize: Integer): Boolean;var  Dummy: Integer;begin  Result := InsertDebugDataIntoExecutableFile(ExecutableFileName, BinDebug, LinkerBugUnit,    MapFileSize, JclDebugDataSize, Dummy);end;function InsertDebugDataIntoExecutableFile(const ExecutableFileName: TFileName;  BinDebug: TJclBinDebugGenerator; out LinkerBugUnit: string;  out MapFileSize, JclDebugDataSize, LineNumberErrors: Integer): Boolean;var  ImageStream: TStream;  NtHeaders32: TImageNtHeaders32;  NtHeaders64: TImageNtHeaders64;  ImageSectionHeaders: TImageSectionHeaderArray;  NtHeadersPosition, ImageSectionHeadersPosition, JclDebugSectionPosition: Int64;  JclDebugSection: TImageSectionHeader;  LastSection: PImageSectionHeader;  VirtualAlignedSize: DWORD;  NeedFill: Integer;  procedure RoundUpToAlignment(var Value: DWORD; Alignment: DWORD);  begin    if (Value mod Alignment) <> 0 then      Value := ((Value div Alignment) + 1) * Alignment;  end;  procedure MovePointerToRawData(AOffset: DWORD);  var    I: Integer;  begin    for I := Low(ImageSectionHeaders) to High(ImageSectionHeaders) do      ImageSectionHeaders[I].PointerToRawData := ImageSectionHeaders[I].PointerToRawData + AOffset;  end;  procedure FillZeros(AStream: TStream; ACount: Integer);  var    I: Integer;    X: array[0..511] of Byte;  begin    if ACount > 0 then    begin      if ACount > Length(X) then        FillChar(X, SizeOf(X), 0)      else        FillChar(X, ACount, 0);      while ACount > 0 do      begin        I := ACount;        if I > SizeOf(X) then          I := SizeOf(X);        AStream.WriteBuffer(X, I);        Dec(ACount, I);      end;    end;  end;  procedure WriteSectionHeaders(AStream: TStream; APosition: Integer);  var    HeaderSize: Integer;  begin    HeaderSize := SizeOf(TImageSectionHeader) * Length(ImageSectionHeaders);    if (AStream.Seek(APosition, soFromBeginning) <> APosition) or       (AStream.Write(ImageSectionHeaders[0], HeaderSize) <> HeaderSize) then      raise EJclPeImageError.CreateRes(@SWriteError);    FillZeros(AStream, ImageSectionHeaders[0].PointerToRawData - AStream.Position);  end;  procedure MoveData(AStream: TStream; AStart, AOffset: Integer);  var    CurPos: Integer;    CurSize: Integer;    Buffer: array of Byte;    StartPos: Integer;  begin    SetLength(Buffer, 1024 * 1024);    CurPos := AStream.Size - Length(Buffer);    StartPos := ImageSectionHeaders[0].PointerToRawData;    while CurPos > StartPos do    begin      if (AStream.Seek(CurPos, soBeginning) <> CurPos) or         (AStream.Read(Buffer[0], Length(Buffer)) <> Length(Buffer)) then        raise EJclPeImageError.CreateRes(@SReadError);      if (AStream.Seek(CurPos + AOffset, soBeginning) <> CurPos + AOffset) or         (AStream.Write(Buffer[0], Length(Buffer)) <> Length(Buffer)) then        raise EJclPeImageError.CreateRes(@SWriteError);      Dec(CurPos, Length(Buffer));    end;    CurSize := Length(Buffer) + CurPos - StartPos;    if (AStream.Seek(StartPos, soBeginning) <> StartPos) or       (AStream.Read(Buffer[0], CurSize) <> CurSize) then      raise EJclPeImageError.CreateRes(@SReadError);    if (AStream.Seek(StartPos + AOffset, soBeginning) <> StartPos + AOffset) or       (AStream.Write(Buffer[0], CurSize) <> CurSize) then      raise EJclPeImageError.CreateRes(@SWriteError);  end;  procedure CheckHeadersSpace(AStream: TStream);  begin    if ImageSectionHeaders[0].PointerToRawData < ImageSectionHeadersPosition +       (SizeOf(TImageSectionHeader) * (Length(ImageSectionHeaders) + 1)) then    begin      MoveData(AStream, ImageSectionHeaders[0].PointerToRawData, NtHeaders64.OptionalHeader.FileAlignment);      MovePointerToRawData(NtHeaders64.OptionalHeader.FileAlignment);      WriteSectionHeaders(AStream, ImageSectionHeadersPosition);    end;  end;begin  MapFileSize := 0;  JclDebugDataSize := 0;  LineNumberErrors := 0;  LinkerBugUnit := '';  if BinDebug.Stream <> nil then  begin    Result := True;    if BinDebug.LinkerBug then    begin      LinkerBugUnit := BinDebug.LinkerBugUnitName;      LineNumberErrors := BinDebug.LineNumberErrors;    end;  end  else    Result := False;  if not Result then    Exit;  ImageStream := TFileStream.Create(ExecutableFileName, fmOpenReadWrite or fmShareExclusive);  try    try      MapFileSize := BinDebug.Stream.Size;      JclDebugDataSize := BinDebug.DataStream.Size;      VirtualAlignedSize := JclDebugDataSize;      // JCLDEBUG      ResetMemory(JclDebugSection, SizeOf(JclDebugSection));      // JCLDEBUG Virtual Size      JclDebugSection.Misc.VirtualSize := JclDebugDataSize;      // JCLDEBUG Raw data size      JclDebugSection.SizeOfRawData := JclDebugDataSize;      // JCLDEBUG Section name      Move(JclDbgDataResName, JclDebugSection.Name, IMAGE_SIZEOF_SHORT_NAME);      // JCLDEBUG Characteristics flags      JclDebugSection.Characteristics := IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;      case PeMapImgTarget(ImageStream, 0) of        taWin32:          begin            NtHeadersPosition := PeMapImgNtHeaders32(ImageStream, 0, NtHeaders32);            Assert(NtHeadersPosition <> -1);            ImageSectionHeadersPosition := PeMapImgSections32(ImageStream, NtHeadersPosition, NtHeaders32, ImageSectionHeaders);            Assert(ImageSectionHeadersPosition <> -1);            // Check whether there is not a section with the name already. If so, return True (0000069)            if PeMapImgFindSection(ImageSectionHeaders, JclDbgDataResName) <> -1 then            begin              Result := True;              Exit;            end;            JclDebugSectionPosition := ImageSectionHeadersPosition + (SizeOf(ImageSectionHeaders[0]) * Length(ImageSectionHeaders));            LastSection := @ImageSectionHeaders[High(ImageSectionHeaders)];            // Increase the number of sections            Inc(NtHeaders32.FileHeader.NumberOfSections);            // JCLDEBUG Virtual Address            JclDebugSection.VirtualAddress := LastSection^.VirtualAddress + LastSection^.Misc.VirtualSize;            // JCLDEBUG Physical Offset            JclDebugSection.PointerToRawData := LastSection^.PointerToRawData + LastSection^.SizeOfRawData;            // JCLDEBUG section rounding :            RoundUpToAlignment(JclDebugSection.VirtualAddress, NtHeaders32.OptionalHeader.SectionAlignment);            RoundUpToAlignment(JclDebugSection.PointerToRawData, NtHeaders32.OptionalHeader.FileAlignment);            RoundUpToAlignment(JclDebugSection.SizeOfRawData, NtHeaders32.OptionalHeader.FileAlignment);            // Size of virtual data area            RoundUpToAlignment(VirtualAlignedSize, NtHeaders32.OptionalHeader.SectionAlignment);            // Update Size of Image            Inc(NtHeaders32.OptionalHeader.SizeOfImage, VirtualAlignedSize);            // Update Initialized data size            Inc(NtHeaders32.OptionalHeader.SizeOfInitializedData, JclDebugSection.SizeOfRawData);            // write NT Headers 32            if (ImageStream.Seek(NtHeadersPosition, soBeginning) <> NtHeadersPosition) or              (ImageStream.Write(NtHeaders32, SizeOf(NtHeaders32)) <> SizeOf(NtHeaders32)) then              raise EJclPeImageError.CreateRes(@SWriteError);          end;        taWin64:          begin            NtHeadersPosition := PeMapImgNtHeaders64(ImageStream, 0, NtHeaders64);            Assert(NtHeadersPosition <> -1);            ImageSectionHeadersPosition := PeMapImgSections64(ImageStream, NtHeadersPosition, NtHeaders64, ImageSectionHeaders);            Assert(ImageSectionHeadersPosition <> -1);            // Check whether there is not a section with the name already. If so, return True (0000069)            if PeMapImgFindSection(ImageSectionHeaders, JclDbgDataResName) <> -1 then            begin              Result := True;              Exit;            end;            // Check if there is enough space for additional header            CheckHeadersSpace(ImageStream);            JclDebugSectionPosition := ImageSectionHeadersPosition + (SizeOf(ImageSectionHeaders[0]) * Length(ImageSectionHeaders));            LastSection := @ImageSectionHeaders[High(ImageSectionHeaders)];            // Increase the number of sections            Inc(NtHeaders64.FileHeader.NumberOfSections);            // JCLDEBUG Virtual Address            JclDebugSection.VirtualAddress := LastSection^.VirtualAddress + LastSection^.Misc.VirtualSize;            // JCLDEBUG Physical Offset            JclDebugSection.PointerToRawData := LastSection^.PointerToRawData + LastSection^.SizeOfRawData;            // JCLDEBUG section rounding :            RoundUpToAlignment(JclDebugSection.VirtualAddress, NtHeaders64.OptionalHeader.SectionAlignment);            RoundUpToAlignment(JclDebugSection.PointerToRawData, NtHeaders64.OptionalHeader.FileAlignment);            RoundUpToAlignment(JclDebugSection.SizeOfRawData, NtHeaders64.OptionalHeader.FileAlignment);            // Size of virtual data area            RoundUpToAlignment(VirtualAlignedSize, NtHeaders64.OptionalHeader.SectionAlignment);            // Update Size of Image            Inc(NtHeaders64.OptionalHeader.SizeOfImage, VirtualAlignedSize);            // Update Initialized data size            Inc(NtHeaders64.OptionalHeader.SizeOfInitializedData, JclDebugSection.SizeOfRawData);            // write NT Headers 64            if (ImageStream.Seek(NtHeadersPosition, soBeginning) <> NtHeadersPosition) or              (ImageStream.Write(NtHeaders64, SizeOf(NtHeaders64)) <> SizeOf(NtHeaders64)) then              raise EJclPeImageError.CreateRes(@SWriteError);          end;      else        Result := False;        Exit;      end;      // write section header      if (ImageStream.Seek(JclDebugSectionPosition, soBeginning) <> JclDebugSectionPosition) or        (ImageStream.Write(JclDebugSection, SizeOf(JclDebugSection)) <> SizeOf(JclDebugSection)) then        raise EJclPeImageError.CreateRes(@SWriteError);      // Fill data to alignment      NeedFill := INT_PTR(JclDebugSection.SizeOfRawData) - JclDebugDataSize;      // Note: Delphi linker seems to generate incorrect (unaligned) size of      // the executable when adding TD32 debug data so the position could be      // behind the size of the file then.      ImageStream.Seek({0 +} JclDebugSection.PointerToRawData, soBeginning);      ImageStream.CopyFrom(BinDebug.DataStream, 0);      FillZeros(ImageStream, NeedFill);    except      Result := False;    end;  finally    ImageStream.Free;  end;end;//=== { TJclBinDebugGenerator } ==============================================constructor TJclBinDebugGenerator.Create(const MapFileName: TFileName; Module: HMODULE);begin  inherited Create(MapFileName, Module);  FDataStream := TMemoryStream.Create;  FMapFileName := MapFileName;  if FStream <> nil then    CreateData;end;destructor TJclBinDebugGenerator.Destroy;begin  FreeAndNil(FDataStream);  inherited Destroy;end;{$OVERFLOWCHECKS OFF}function TJclBinDebugGenerator.CalculateCheckSum: Boolean;var  Header: PJclDbgHeader;  P, EndData: PAnsiChar;  CheckSum: Integer;begin  Result := DataStream.Size >= SizeOf(TJclDbgHeader);  if Result then  begin    P := DataStream.Memory;    EndData := P + DataStream.Size;    Header := PJclDbgHeader(P);    CheckSum := 0;    Header^.CheckSum := 0;    Header^.CheckSumValid := True;    while P < EndData do    begin      Inc(CheckSum, PInteger(P)^);      Inc(PInteger(P));    end;    Header^.CheckSum := CheckSum;  end;end;{$IFDEF OVERFLOWCHECKS_ON}{$OVERFLOWCHECKS ON}{$ENDIF OVERFLOWCHECKS_ON}procedure TJclBinDebugGenerator.CreateData;var  {$IFDEF SUPPORTS_GENERICS}  WordList: TDictionary<string, Integer>;  {$ELSE}  WordList: TStringList;  {$ENDIF SUPPORTS_GENERICS}  WordStream: TMemoryStream;  LastSegmentID: Word;  LastSegmentStored: Boolean;  function PosLastNameSep(const S: string): Integer;  var    InGeneric: Integer;  begin    // Unit.Name.ProcName                             => "Unit.Name" + "ProcName"    // Unit.Name..ClassName                           => "UnitName" + ".ClassName"    // Unit.Name.Class<Unit.Name.OtherClass>.ProcName => "Unit.Name.Class<Unit.Name.OtherClass>" + "ProcName"    InGeneric := 0;    for Result := Length(S) downto 1 do    begin      case S[Result] of        '.':          if InGeneric = 0 then            if (Result = 1) or (S[Result - 1] <> '.') then              Exit;        '>':          Inc(InGeneric);        '<':          Dec(InGeneric);      end;    end;    Result := 0;  end;  function AddWord(const S: string): Integer;  var    {$IFDEF SUPPORTS_GENERICS}    LowerS: string;    {$ELSE}    N: Integer;    {$ENDIF SUPPORTS_GENERICS}    E: AnsiString;  begin    if S = '' then    begin      Result := 0;      Exit;    end;    {$IFDEF SUPPORTS_GENERICS}    LowerS := AnsiLowerCase(S);    if not WordList.TryGetValue(LowerS, Result) then    begin      Result := WordStream.Position;      E := EncodeNameString(S);      WordStream.Write(E[1], Length(E));      WordList.Add(LowerS, Result);    end;    {$ELSE} // for large map files this is very slow    N := WordList.IndexOf(S);    if N = -1 then    begin      Result := WordStream.Position;      E := EncodeNameString(S);      WordStream.Write(E[1], Length(E));      WordList.AddObject(S, TObject(Result));    end    else      Result := DWORD(WordList.Objects[N]);    {$ENDIF SUPPORTS_GENERICS}    Inc(Result);  end;  procedure WriteValue(Value: Integer);  var    L: Integer;    D: DWORD;    P: array [1..5] of Byte;  begin    D := Value and $FFFFFFFF;    L := 0;    while D > $7F do    begin      Inc(L);      P[L] := (D and $7F) or $80;      D := D shr 7;    end;    Inc(L);    P[L] := (D and $7F);    FDataStream.Write(P, L);  end;  procedure WriteValueOfs(Value: Integer; var LastValue: Integer);  begin    WriteValue(Value - LastValue);    LastValue := Value;  end;  function IsSegmentStored(SegID: Word): Boolean;  var    SegIndex: Integer;    GroupName: string;  begin    if SegID <> LastSegmentID then    begin      LastSegmentID := $FFFF;      LastSegmentStored := False;      for SegIndex := Low(FSegmentClasses) to High(FSegmentClasses) do        if FSegmentClasses[SegIndex].Segment = SegID then      begin        LastSegmentID := FSegmentClasses[SegIndex].Segment;        GroupName := MapStringCacheToStr(FSegmentClasses[SegIndex].GroupName);        LastSegmentStored := (GroupName = 'CODE') or (GroupName = 'ICODE');        Break;      end;    end;    Result := LastSegmentStored;  end;const  AlignBytes: array[0..2] of Byte = (0, 0, 0);var  FileHeader: TJclDbgHeader;  I, D: Integer;  S: string;  L1, L2, L3: Integer;  FirstWord, SecondWord: Integer;  WordStreamSize, DataStreamSize: Int64;begin  LastSegmentID := $FFFF;  WordStream := TMemoryStream.Create;  {$IFDEF SUPPORTS_GENERICS}  WordList := TDictionary<string, Integer>.Create(Length(FSourceNames) + Length(FProcNames));  {$ELSE}  WordList := TStringList.Create;  {$ENDIF SUPPORTS_GENERICS}  try    {$IFNDEF SUPPORTS_GENERICS}    WordList.Sorted := True;    WordList.Duplicates := dupError;    {$ENDIF ~SUPPORTS_GENERICS}    WordStream.SetSize((Length(FSourceNames) + Length(FProcNames)) * 40); // take an average of 40 chars per identifier    FileHeader.Signature := JclDbgDataSignature;    FileHeader.Version := JclDbgHeaderVersion;    FileHeader.CheckSum := 0;    FileHeader.CheckSumValid := False;    FileHeader.ModuleName := AddWord(PathExtractFileNameNoExt(FMapFileName));    FDataStream.WriteBuffer(FileHeader, SizeOf(FileHeader));    FileHeader.Units := FDataStream.Position;    L1 := 0;    L2 := 0;    for I := 0 to Length(FSegments) - 1 do      if IsSegmentStored(FSegments[I].Segment) then      begin        WriteValueOfs(FSegments[I].StartVA, L1);        WriteValueOfs(AddWord(MapStringCacheToModuleName(FSegments[I].UnitName)), L2);      end;    WriteValue(MaxInt);    FileHeader.SourceNames := FDataStream.Position;    L1 := 0;    L2 := 0;    for I := 0 to Length(FSourceNames) - 1 do      if IsSegmentStored(FSourceNames[I].Segment) then      begin        // FSourceNames[] is sorted by VA, so if the source file name is the same as the previous        // we don't need to store it because the VA will be matched by the previous entry.        // This removes a lot of "Generics.Collections.pas" entries.        S := MapStringCacheToStr(FSourceNames[I].ProcName);        if (I = 0) or (FSourceNames[I - 1].ProcName.CachedValue <> S) then        begin          WriteValueOfs(FSourceNames[I].VA, L1);          WriteValueOfs(AddWord(S), L2);        end;      end;    WriteValue(MaxInt);    FileHeader.Symbols := FDataStream.Position;    L1 := 0;    L2 := 0;    L3 := 0;    for I := 0 to Length(FProcNames) - 1 do      if IsSegmentStored(FProcNames[I].Segment) then      begin        WriteValueOfs(FProcNames[I].VA, L1);        // MAP files generated by C++Builder have spaces in their names        S := MapStringCacheToStr(FProcNames[I].ProcName, True);        D := PosLastNameSep(S);        if D = 1 then        begin          FirstWord := 0;          SecondWord := 0;        end        else        if D = 0 then        begin          FirstWord := AddWord(S);          SecondWord := 0;        end        else        begin          FirstWord := AddWord(Copy(S, 1, D - 1));          SecondWord := AddWord(Copy(S, D + 1, Length(S)));        end;        WriteValueOfs(FirstWord, L2);        WriteValueOfs(SecondWord, L3);      end;    WriteValue(MaxInt);    FileHeader.LineNumbers := FDataStream.Position;    L1 := 0;    L2 := 0;    for I := 0 to Length(FLineNumbers) - 1 do      if IsSegmentStored(FLineNumbers[I].Segment) then      begin        WriteValueOfs(FLineNumbers[I].VA, L1);        WriteValueOfs(FLineNumbers[I].LineNumber, L2);      end;    WriteValue(MaxInt);    FileHeader.Words := FDataStream.Position;    // Calculate and allocate the required size in advance instead of reallocating on the fly.    WordStreamSize := WordStream.Position;    DataStreamSize := FDataStream.Position + WordStreamSize;    DataStreamSize := DataStreamSize + (4 - (DataStreamSize and $3));    FDataStream.Size := DataStreamSize; // set capacity    WordStream.Position := 0;    FDataStream.CopyFrom(WordStream, WordStreamSize);    // Align to 4 bytes    FDataStream.WriteBuffer(AlignBytes, 4 - (FDataStream.Position and $3));    if FDataStream.Size <> FDataStream.Position then // just in case something changed without adjusting the size calculation      FDataStream.Size := FDataStream.Position;    // Update the file header    FDataStream.Seek(0, soBeginning);    FDataStream.WriteBuffer(FileHeader, SizeOf(FileHeader));  finally    WordStream.Free;    WordList.Free;  end;end;//=== { TJclBinDebugScanner } ================================================constructor TJclBinDebugScanner.Create(AStream: TCustomMemoryStream; CacheData, CacheProcNames: Boolean);begin  inherited Create;  FCacheData := CacheData;  FCacheProcNames := CacheProcNames;  FStream := AStream;  CheckFormat;end;procedure TJclBinDebugScanner.CacheLineNumbers;var  P: Pointer;  Value, LineNumber, C, Ln: Integer;  CurrVA: DWORD;begin  if FLineNumbers = nil then  begin    LineNumber := 0;    CurrVA := 0;    C := 0;    Ln := 0;    P := MakePtr(PJclDbgHeader(FStream.Memory)^.LineNumbers);    Value := 0;    while ReadValue(P, Value) do    begin      Inc(CurrVA, Value);      ReadValue(P, Value);      Inc(LineNumber, Value);      if C = Ln then      begin        if Ln < 64 then          Ln := 64        else          Ln := Ln + Ln div 4;        SetLength(FLineNumbers, Ln);      end;      FLineNumbers[C].VA := CurrVA;      FLineNumbers[C].LineNumber := LineNumber;      Inc(C);    end;    SetLength(FLineNumbers, C);  end;end;procedure TJclBinDebugScanner.CacheProcNames;var  P: Pointer;  Value, FirstWord, SecondWord, C, Ln: Integer;  CurrAddr: DWORD;begin  if FProcNames = nil then  begin    FirstWord := 0;    SecondWord := 0;    CurrAddr := 0;    C := 0;    Ln := 0;    P := MakePtr(PJclDbgHeader(FStream.Memory)^.Symbols);    Value := 0;    while ReadValue(P, Value) do    begin      Inc(CurrAddr, Value);      ReadValue(P, Value);      Inc(FirstWord, Value);      ReadValue(P, Value);      Inc(SecondWord, Value);      if C = Ln then      begin        if Ln < 64 then          Ln := 64        else          Ln := Ln + Ln div 4;        SetLength(FProcNames, Ln);      end;      FProcNames[C].Addr := CurrAddr;      FProcNames[C].FirstWord := FirstWord;      FProcNames[C].SecondWord := SecondWord;      if FCacheProcNames then      begin        if (FirstWord <> 0) and (SecondWord <> 0) then          FProcNames[C].Text := DataToStr(FirstWord) + '.' + DataToStr(SecondWord)        else if FirstWord <> 0 then          FProcNames[C].Text := DataToStr(FirstWord)        else          FProcNames[C].Text := '';      end      else        FProcNames[C].Text := '';      Inc(C);    end;    SetLength(FProcNames, C);  end;end;{$OVERFLOWCHECKS OFF}procedure TJclBinDebugScanner.CheckFormat;var  CheckSum: Integer;  Data, EndData: PAnsiChar;  Header: PJclDbgHeader;begin  Data := FStream.Memory;  Header := PJclDbgHeader(Data);  FValidFormat := (Data <> nil) and (FStream.Size > SizeOf(TJclDbgHeader)) and    (FStream.Size mod 4 = 0) and    (Header^.Signature = JclDbgDataSignature) and (Header^.Version = JclDbgHeaderVersion);  if FValidFormat and Header^.CheckSumValid then  begin    CheckSum := -Header^.CheckSum;    EndData := Data + FStream.Size;    while Data < EndData do    begin      Inc(CheckSum, PInteger(Data)^);      Inc(PInteger(Data));    end;    CheckSum := (CheckSum shr 8) or (CheckSum shl 24);    FValidFormat := (CheckSum = Header^.CheckSum);  end;end;{$IFDEF OVERFLOWCHECKS_ON}{$OVERFLOWCHECKS ON}{$ENDIF OVERFLOWCHECKS_ON}function TJclBinDebugScanner.DataToStr(A: Integer): string;var  P: PAnsiChar;begin  if A = 0 then    Result := ''  else  begin    P := PAnsiChar(TJclAddr(FStream.Memory) + TJclAddr(A) + TJclAddr(PJclDbgHeader(FStream.Memory)^.Words) - 1);    Result := DecodeNameString(P);  end;end;function TJclBinDebugScanner.GetModuleName: string;begin  Result := DataToStr(PJclDbgHeader(FStream.Memory)^.ModuleName);end;function TJclBinDebugScanner.IsModuleNameValid(const Name: TFileName): Boolean;begin  Result := AnsiSameText(ModuleName, PathExtractFileNameNoExt(Name));end;function TJclBinDebugScanner.LineNumberFromAddr(Addr: DWORD): Integer;var  Dummy: Integer;begin  Result := LineNumberFromAddr(Addr, Dummy);end;function TJclBinDebugScanner.LineNumberFromAddr(Addr: DWORD; out Offset: Integer): Integer;var  P: Pointer;  Value, LineNumber: Integer;  CurrVA, ModuleStartVA, ItemVA: DWORD;begin  ModuleStartVA := ModuleStartFromAddr(Addr);  LineNumber := 0;  Offset := 0;  if FCacheData then  begin    CacheLineNumbers;    for Value := Length(FLineNumbers) - 1 downto 0 do      if FLineNumbers[Value].VA <= Addr then      begin        if FLineNumbers[Value].VA >= ModuleStartVA then        begin          LineNumber := FLineNumbers[Value].LineNumber;          Offset := Addr - FLineNumbers[Value].VA;        end;        Break;      end;  end  else  begin    P := MakePtr(PJclDbgHeader(FStream.Memory)^.LineNumbers);    CurrVA := 0;    ItemVA := 0;    while ReadValue(P, Value) do    begin      Inc(CurrVA, Value);      if Addr < CurrVA then      begin        if ItemVA < ModuleStartVA then        begin          LineNumber := 0;          Offset := 0;        end;        Break;      end      else      begin        ItemVA := CurrVA;        ReadValue(P, Value);        Inc(LineNumber, Value);        Offset := Addr - CurrVA;      end;    end;  end;  Result := LineNumber;end;function TJclBinDebugScanner.MakePtr(A: Integer): Pointer;begin  Result := Pointer(TJclAddr(FStream.Memory) + TJclAddr(A));end;function TJclBinDebugScanner.ModuleNameFromAddr(Addr: DWORD): string;var  Value, Name: Integer;  StartAddr: DWORD;  P: Pointer;begin  P := MakePtr(PJclDbgHeader(FStream.Memory)^.Units);  Name := 0;  StartAddr := 0;  Value := 0;  while ReadValue(P, Value) do  begin    Inc(StartAddr, Value);    if Addr < StartAddr then      Break    else    begin      ReadValue(P, Value);      Inc(Name, Value);    end;  end;  Result := DataToStr(Name);end;function TJclBinDebugScanner.ModuleStartFromAddr(Addr: DWORD): DWORD;var  Value: Integer;  StartAddr, ModuleStartAddr: DWORD;  P: Pointer;begin  P := MakePtr(PJclDbgHeader(FStream.Memory)^.Units);  StartAddr := 0;  ModuleStartAddr := DWORD(-1);  Value := 0;  while ReadValue(P, Value) do  begin    Inc(StartAddr, Value);    if Addr < StartAddr then      Break    else    begin      ReadValue(P, Value);      ModuleStartAddr := StartAddr;    end;  end;  Result := ModuleStartAddr;end;function TJclBinDebugScanner.ProcNameFromAddr(Addr: DWORD): string;var  Dummy: Integer;begin  Result := ProcNameFromAddr(Addr, Dummy);end;function TJclBinDebugScanner.ProcNameFromAddr(Addr: DWORD; out Offset: Integer): string;var  P: Pointer;  Value, FirstWord, SecondWord: Integer;  CurrAddr, ModuleStartAddr, ItemAddr: DWORD;begin  ModuleStartAddr := ModuleStartFromAddr(Addr);  FirstWord := 0;  SecondWord := 0;  Offset := 0;  if FCacheData then  begin    CacheProcNames;    for Value := Length(FProcNames) - 1 downto 0 do      if FProcNames[Value].Addr <= Addr then      begin        if FProcNames[Value].Addr >= ModuleStartAddr then        begin          FirstWord := FProcNames[Value].FirstWord;          SecondWord := FProcNames[Value].SecondWord;          Offset := Addr - FProcNames[Value].Addr;        end;        Break;      end;  end  else  begin    P := MakePtr(PJclDbgHeader(FStream.Memory)^.Symbols);    CurrAddr := 0;    ItemAddr := 0;    while ReadValue(P, Value) do    begin      Inc(CurrAddr, Value);      if Addr < CurrAddr then      begin        if ItemAddr < ModuleStartAddr then        begin          FirstWord := 0;          SecondWord := 0;          Offset := 0;        end;        Break;      end      else      begin        ItemAddr := CurrAddr;        ReadValue(P, Value);        Inc(FirstWord, Value);        ReadValue(P, Value);        Inc(SecondWord, Value);        Offset := Addr - CurrAddr;      end;    end;  end;  if FirstWord <> 0 then  begin    Result := DataToStr(FirstWord);    if SecondWord <> 0 then      Result := Result + '.' + DataToStr(SecondWord);  end  else    Result := '';end;class function TJclBinDebugScanner.ReadValue(var P: Pointer; var Value: Integer): Boolean;var  N: Integer;  I: Integer;  B: Byte;begin  N := 0;  I := 0;  repeat    B := PByte(P)^;    Inc(PByte(P));    Inc(N, (B and $7F) shl I);    Inc(I, 7);  until B and $80 = 0;  Value := N;  Result := (N <> MaxInt);end;function TJclBinDebugScanner.SourceNameFromAddr(Addr: DWORD): string;var  Value, Name: Integer;  StartAddr, ModuleStartAddr, ItemAddr: DWORD;  P: Pointer;  Found: Boolean;begin  ModuleStartAddr := ModuleStartFromAddr(Addr);  P := MakePtr(PJclDbgHeader(FStream.Memory)^.SourceNames);  Name := 0;  StartAddr := 0;  ItemAddr := 0;  Found := False;  Value := 0;  while ReadValue(P, Value) do  begin    Inc(StartAddr, Value);    if Addr < StartAddr then    begin      if ItemAddr < ModuleStartAddr then        Name := 0      else        Found := True;      Break;    end    else    begin      ItemAddr := StartAddr;      ReadValue(P, Value);      Inc(Name, Value);    end;  end;  if Found then    Result := DataToStr(Name)  else    Result := '';end;function TJclBinDebugScanner.VAFromUnitAndProcName(const UnitName, ProcName: string): DWORD;var  P: Pointer;  VA: DWORD;  I, Value: Integer;  FirstWord, SecondWord: Integer;  QualifiedName, S: string;begin  Result := 0;  if (UnitName = '') or (ProcName = '') then    Exit;  QualifiedName := UnitName + '.' + ProcName;  if FCacheData then  begin    CacheProcNames;    for I := Low(FProcNames) to High(FProcNames) do    begin      if FProcNames[I].Text <> '' then        S := FProcNames[I].Text      else      begin        if FProcNames[I].FirstWord = 0 then          Continue;        if (FProcNames[I].FirstWord <> 0) and (FProcNames[I].SecondWord <> 0) then          FProcNames[I].Text := DataToStr(FProcNames[I].FirstWord ) + '.' + DataToStr(FProcNames[I].SecondWord)        else if FProcNames[I].FirstWord <> 0 then          FProcNames[I].Text := DataToStr(FProcNames[I].FirstWord)        else          FProcNames[I].Text := '';      end;      if CompareText(FProcNames[I].Text, QualifiedName) = 0 then      begin        Result := FProcNames[i].Addr;        Break;      end;    end;  end  else  begin    P := MakePtr(PJclDbgHeader(FStream.Memory)^.Symbols);    VA := 0;    FirstWord := 0;    SecondWord := 0;    while ReadValue(P, Value) do    begin      Inc(VA, Value);      ReadValue(P, Value);      Inc(FirstWord, Value);      ReadValue(P, Value);      Inc(SecondWord, Value);      if FirstWord = 0 then        Continue;      S := DataToStr(FirstWord);      if SecondWord <> 0 then        S := S + '.' + DataToStr(SecondWord);      if CompareText(S, QualifiedName) = 0 then      begin        Result := VA;        Break;      end;    end;  end;end;//=== { TJclLocationInfoEx } =================================================constructor TJclLocationInfoEx.Create(AParent: TJclCustomLocationInfoList; Address: Pointer);var  Options: TJclLocationInfoListOptions;begin  inherited Create;  FAddress := Address;  FParent := AParent;  if Assigned(FParent) then    Options := FParent.Options  else    Options := [];  Fill(Options);end;procedure TJclLocationInfoEx.AssignTo(Dest: TPersistent);begin  if Dest is TJclLocationInfoEx then  begin    TJclLocationInfoEx(Dest).FAddress := FAddress;    TJclLocationInfoEx(Dest).FBinaryFileName := FBinaryFileName;    TJclLocationInfoEx(Dest).FDebugInfo := FDebugInfo;    TJclLocationInfoEx(Dest).FLineNumber := FLineNumber;    TJclLocationInfoEx(Dest).FLineNumberOffsetFromProcedureStart := FLineNumberOffsetFromProcedureStart;    TJclLocationInfoEx(Dest).FModuleName := FModuleName;    TJclLocationInfoEx(Dest).FOffsetFromLineNumber := FOffsetFromLineNumber;    TJclLocationInfoEx(Dest).FOffsetFromProcName := FOffsetFromProcName;    TJclLocationInfoEx(Dest).FProcedureName := FProcedureName;    TJclLocationInfoEx(Dest).FSourceName := FSourceName;    TJclLocationInfoEx(Dest).FSourceUnitName := FSourceUnitName;    TJclLocationInfoEx(Dest).FUnitVersionDateTime := FUnitVersionDateTime;    TJclLocationInfoEx(Dest).FUnitVersionExtra := FUnitVersionExtra;    TJclLocationInfoEx(Dest).FUnitVersionLogPath := FUnitVersionLogPath;    TJclLocationInfoEx(Dest).FUnitVersionRCSfile := FUnitVersionRCSfile;    TJclLocationInfoEx(Dest).FUnitVersionRevision := FUnitVersionRevision;    TJclLocationInfoEx(Dest).FVAddress := FVAddress;    TJclLocationInfoEx(Dest).FValues := FValues;  end  else    inherited AssignTo(Dest);end;procedure TJclLocationInfoEx.Clear;begin  FAddress := nil;  Fill([]);end;procedure TJclLocationInfoEx.Fill(AOptions: TJclLocationInfoListOptions);var  Info, StartProcInfo: TJclLocationInfo;  FixedProcedureName: string;  Module: HMODULE;  {$IFDEF UNITVERSIONING}  I: Integer;  UnitVersion: TUnitVersion;  UnitVersioning: TUnitVersioning;  UnitVersioningModule: TUnitVersioningModule;  {$ENDIF UNITVERSIONING}begin  FValues := [];  if liloAutoGetAddressInfo in AOptions then  begin    Module := ModuleFromAddr(FAddress);    FVAddress := Pointer(TJclAddr(FAddress) - TJclAddr(Module) - ModuleCodeOffset);    FModuleName := ExtractFileName(GetModulePath(Module));  end  else  begin    {$IFDEF UNITVERSIONING}    Module := 0;    {$ENDIF UNITVERSIONING}    FVAddress := nil;    FModuleName := '';  end;  if (liloAutoGetLocationInfo in AOptions) and GetLocationInfo(FAddress, Info) then  begin    FValues := FValues + [lievLocationInfo];    FOffsetFromProcName := Info.OffsetFromProcName;    FSourceUnitName := Info.UnitName;    FixedProcedureName := Info.ProcedureName;    if Pos(Info.UnitName + '.', FixedProcedureName) = 1 then      FixedProcedureName := Copy(FixedProcedureName, Length(Info.UnitName) + 2, Length(FixedProcedureName) - Length(Info.UnitName) - 1);    FProcedureName := FixedProcedureName;    FSourceName := Info.SourceName;    FLineNumber := Info.LineNumber;    if FLineNumber > 0 then      FOffsetFromLineNumber := Info.OffsetFromLineNumber    else      FOffsetFromLineNumber := 0;    if GetLocationInfo(Pointer(TJclAddr(Info.Address) -      Cardinal(Info.OffsetFromProcName)), StartProcInfo) and (StartProcInfo.LineNumber > 0) then    begin      FLineNumberOffsetFromProcedureStart := Info.LineNumber - StartProcInfo.LineNumber;      FValues := FValues + [lievProcedureStartLocationInfo];    end    else      FLineNumberOffsetFromProcedureStart := 0;    FDebugInfo := Info.DebugInfo;    FBinaryFileName := Info.BinaryFileName;  end  else  begin    FOffsetFromProcName := 0;    FSourceUnitName := '';    FProcedureName := '';    FSourceName := '';    FLineNumber := 0;    FOffsetFromLineNumber := 0;    FLineNumberOffsetFromProcedureStart := 0;    FDebugInfo := nil;    FBinaryFileName := '';  end;  FUnitVersionDateTime := 0;  FUnitVersionLogPath := '';  FUnitVersionRCSfile := '';  FUnitVersionRevision := '';  {$IFDEF UNITVERSIONING}  if (liloAutoGetUnitVersionInfo in AOptions) and (FSourceName <> '') then  begin    if not (liloAutoGetAddressInfo in AOptions) then      Module := ModuleFromAddr(FAddress);    UnitVersioning := GetUnitVersioning;    for I := 0 to UnitVersioning.ModuleCount - 1 do    begin      UnitVersioningModule := UnitVersioning.Modules[I];      if UnitVersioningModule.Instance = Module then      begin        UnitVersion := UnitVersioningModule.FindUnit(FSourceName);        if Assigned(UnitVersion) then        begin          FUnitVersionDateTime := UnitVersion.DateTime;          FUnitVersionLogPath := UnitVersion.LogPath;          FUnitVersionRCSfile := UnitVersion.RCSfile;          FUnitVersionRevision := UnitVersion.Revision;          FValues := FValues + [lievUnitVersionInfo];          Break;        end;      end;      if lievUnitVersionInfo in FValues then        Break;    end;  end;  {$ENDIF UNITVERSIONING}end;{ TODO -oUSc : Include... better as function than property? }function TJclLocationInfoEx.GetAsString: string;const  IncludeStartProcLineOffset = True;  IncludeAddressOffset = True;  IncludeModuleName = True;var  IncludeVAddress: Boolean;  OffsetStr, StartProcOffsetStr: string;begin  IncludeVAddress := True;  OffsetStr := '';  if lievLocationInfo in FValues then  begin    if LineNumber > 0 then    begin      if IncludeStartProcLineOffset and (lievProcedureStartLocationInfo in FValues) then        StartProcOffsetStr := Format(' + %d', [LineNumberOffsetFromProcedureStart])      else        StartProcOffsetStr := '';      if IncludeAddressOffset then      begin        if OffsetFromLineNumber >= 0 then          OffsetStr := Format(' + $%x', [OffsetFromLineNumber])        else          OffsetStr := Format(' - $%x', [-OffsetFromLineNumber])      end;      Result := Format('[%p] %s.%s (Line %u, "%s"%s)%s', [Address, SourceUnitName, ProcedureName, LineNumber,        SourceName, StartProcOffsetStr, OffsetStr]);    end    else    begin      if IncludeAddressOffset then        OffsetStr := Format(' + $%x', [OffsetFromProcName]);      if SourceUnitName <> '' then        Result := Format('[%p] %s.%s%s', [Address, SourceUnitName, ProcedureName, OffsetStr])      else        Result := Format('[%p] %s%s', [Address, ProcedureName, OffsetStr]);    end;  end  else  begin    Result := Format('[%p]', [Address]);    IncludeVAddress := True;  end;  if IncludeVAddress or IncludeModuleName then  begin    if IncludeVAddress then    begin      OffsetStr := Format('(%p) ', [VAddress]);      Result := OffsetStr + Result;    end;    if IncludeModuleName then      Insert(Format('{%-12s}', [ModuleName]), Result, 11 {$IFDEF CPUX64}+ 8{$ENDIF});  end;end;//=== { TJclCustomLocationInfoList } =========================================constructor TJclCustomLocationInfoList.Create;begin  inherited Create;  FItemClass := TJclLocationInfoEx;  FItems := TObjectList.Create;  FOptions := [];end;destructor TJclCustomLocationInfoList.Destroy;begin  FItems.Free;  inherited Destroy;end;procedure TJclCustomLocationInfoList.AddStackInfoList(AStackInfoList: TObject);var  I: Integer;begin  TJclStackInfoList(AStackInfoList).ForceStackTracing;  for I := 0 to TJclStackInfoList(AStackInfoList).Count - 1 do    InternalAdd(TJclStackInfoList(AStackInfoList)[I].CallerAddr);end;procedure TJclCustomLocationInfoList.AssignTo(Dest: TPersistent);var  I: Integer;begin  if Dest is TJclCustomLocationInfoList then  begin    TJclCustomLocationInfoList(Dest).Clear;    for I := 0 to Count - 1 do      TJclCustomLocationInfoList(Dest).InternalAdd(nil).Assign(TJclLocationInfoEx(FItems[I]));  end  else    inherited AssignTo(Dest);end;procedure TJclCustomLocationInfoList.Clear;begin  FItems.Clear;end;function TJclCustomLocationInfoList.GetAsString: string;var  I: Integer;  Strings: TStringList;begin  Strings := TStringList.Create;  try    for I := 0 to Count - 1 do      Strings.Add(TJclLocationInfoEx(FItems[I]).AsString);    Result := Strings.Text;  finally    Strings.Free;  end;end;function TJclCustomLocationInfoList.GetCount: Integer;begin  Result := FItems.Count;end;function TJclCustomLocationInfoList.InternalAdd(Addr: Pointer): TJclLocationInfoEx;begin  FItems.Add(FItemClass.Create(Self, Addr));  Result := TJclLocationInfoEx(FItems.Last);end;//=== { TJclLocationInfoList } ===============================================function TJclLocationInfoList.Add(Addr: Pointer): TJclLocationInfoEx;begin  Result := InternalAdd(Addr);end;constructor TJclLocationInfoList.Create;begin  inherited Create;  FOptions := [liloAutoGetAddressInfo, liloAutoGetLocationInfo, liloAutoGetUnitVersionInfo];end;function TJclLocationInfoList.GetItems(AIndex: Integer): TJclLocationInfoEx;begin  Result := TJclLocationInfoEx(FItems[AIndex]);end;//=== { TJclDebugInfoSource } ================================================constructor TJclDebugInfoSource.Create(AModule: HMODULE);var  MemInfo: TMemoryBasicInformation;begin  FModule := AModule;  FModuleCodeSize := 0;  if VirtualQuery(Pointer(TJclAddr(FModule) + ModuleCodeOffset), MemInfo, SizeOf(MemInfo)) = SizeOf(MemInfo) then    FModuleCodeSize := MemInfo.RegionSize;end;function TJclDebugInfoSource.GetFileName: TFileName;begin  Result := GetModulePath(FModule);end;function TJclDebugInfoSource.VAFromAddr(const Addr: Pointer): DWORD;begin  Result := DWORD(TJclAddr(Addr) - TJclAddr(FModule) - ModuleCodeOffset);end;function TJclDebugInfoSource.AddrFromVA(const VA: DWORD): Pointer;begin  Result := Pointer(TJclAddr(VA) + TJclAddr(FModule) + ModuleCodeOffset);end;//=== { TJclDebugInfoList } ==================================================var  DebugInfoList: TJclDebugInfoList = nil;  InfoSourceClassList: TList = nil;  DebugInfoCritSect: TJclCriticalSection;procedure NeedDebugInfoList;begin  if DebugInfoList = nil then    DebugInfoList := TJclDebugInfoList.Create;end;function TJclDebugInfoList.CreateDebugInfo(const Module: HMODULE): TJclDebugInfoSource;var  I: Integer;begin  NeedInfoSourceClassList;  Result := nil;  for I := 0 to InfoSourceClassList.Count - 1 do  begin    Result := TJclDebugInfoSourceClass(InfoSourceClassList.Items[I]).Create(Module);    try      if Result.InitializeSource then        Break      else        FreeAndNil(Result);    except      Result.Free;      raise;    end;  end;end;function TJclDebugInfoList.GetItemFromModule(const Module: HMODULE): TJclDebugInfoSource;var  I: Integer;  TempItem: TJclDebugInfoSource;begin  Result := nil;  if Module = 0 then    Exit;  for I := 0 to Count - 1 do  begin    TempItem := Items[I];    if TempItem.Module = Module then    begin      Result := TempItem;      Break;    end;  end;  if Result = nil then  begin    Result := CreateDebugInfo(Module);    if Result <> nil then      Add(Result);  end;end;function TJclDebugInfoList.GetItems(Index: TJclListSize): TJclDebugInfoSource;begin  Result := TJclDebugInfoSource(Get(Index));end;function TJclDebugInfoList.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;var  Item: TJclDebugInfoSource;begin  ResetMemory(Info, SizeOf(Info));  Item := ItemFromModule[CachedModuleFromAddr(Addr)];  if Item <> nil then    Result := Item.GetLocationInfo(Addr, Info)  else    Result := False;end;class procedure TJclDebugInfoList.NeedInfoSourceClassList;begin  if not Assigned(InfoSourceClassList) then  begin    InfoSourceClassList := TList.Create;    {$IFNDEF DEBUG_NO_BINARY}    InfoSourceClassList.Add(Pointer(TJclDebugInfoBinary));    {$ENDIF !DEBUG_NO_BINARY}    {$IFNDEF DEBUG_NO_TD32}    {$IFNDEF WINSCP}    InfoSourceClassList.Add(Pointer(TJclDebugInfoTD32));    {$ENDIF ~WINSCP}    {$ENDIF !DEBUG_NO_TD32}    {$IFNDEF DEBUG_NO_MAP}    InfoSourceClassList.Add(Pointer(TJclDebugInfoMap));    {$ENDIF !DEBUG_NO_MAP}    {$IFNDEF DEBUG_NO_SYMBOLS}    InfoSourceClassList.Add(Pointer(TJclDebugInfoSymbols));    {$ENDIF !DEBUG_NO_SYMBOLS}    {$IFNDEF DEBUG_NO_EXPORTS}    InfoSourceClassList.Add(Pointer(TJclDebugInfoExports));    {$ENDIF !DEBUG_NO_EXPORTS}  end;end;class procedure TJclDebugInfoList.RegisterDebugInfoSource(  const InfoSourceClass: TJclDebugInfoSourceClass);begin  NeedInfoSourceClassList;  InfoSourceClassList.Add(Pointer(InfoSourceClass));end;class procedure TJclDebugInfoList.RegisterDebugInfoSourceFirst(  const InfoSourceClass: TJclDebugInfoSourceClass);begin  NeedInfoSourceClassList;  InfoSourceClassList.Insert(0, Pointer(InfoSourceClass));end;class procedure TJclDebugInfoList.UnRegisterDebugInfoSource(  const InfoSourceClass: TJclDebugInfoSourceClass);begin  if Assigned(InfoSourceClassList) then    InfoSourceClassList.Remove(Pointer(InfoSourceClass));end;//=== { TJclDebugInfoMap } ===================================================destructor TJclDebugInfoMap.Destroy;begin  FreeAndNil(FScanner);  inherited Destroy;end;function TJclDebugInfoMap.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;var  VA: DWORD;begin  VA := VAFromAddr(Addr);  with FScanner do  begin    Info.UnitName := ModuleNameFromAddr(VA);    Result := Info.UnitName <> '';    if Result then    begin      Info.Address := Addr;      Info.ProcedureName := ProcNameFromAddr(VA, Info.OffsetFromProcName);      Info.LineNumber := LineNumberFromAddr(VA, Info.OffsetFromLineNumber);      Info.SourceName := SourceNameFromAddr(VA);      Info.DebugInfo := Self;      Info.BinaryFileName := FileName;    end;  end;end;function TJclDebugInfoMap.GetAddress(const UnitName, ProcName: string): Pointer;var  VA: DWORD;begin  Result := nil;  VA := FScanner.VAFromUnitAndProcName(UnitName, ProcName);  if VA <> 0 then    Result := AddrFromVA(VA);end;function TJclDebugInfoMap.InitializeSource: Boolean;var  MapFileName: TFileName;begin  MapFileName := ChangeFileExt(FileName, JclMapFileExtension);  Result := FileExists(MapFileName);  if Result then    FScanner := TJclMapScanner.Create(MapFileName, Module);end;//=== { TJclDebugInfoBinary } ================================================destructor TJclDebugInfoBinary.Destroy;begin  FreeAndNil(FScanner);  FreeAndNil(FStream);  inherited Destroy;end;function TJclDebugInfoBinary.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;var  VA: DWORD;begin  VA := VAFromAddr(Addr);  with FScanner do  begin    Info.UnitName := ModuleNameFromAddr(VA);    Result := Info.UnitName <> '';    if Result then    begin      Info.Address := Addr;      Info.ProcedureName := ProcNameFromAddr(VA, Info.OffsetFromProcName);      Info.LineNumber := LineNumberFromAddr(VA, Info.OffsetFromLineNumber);      Info.SourceName := SourceNameFromAddr(VA);      Info.DebugInfo := Self;      Info.BinaryFileName := FileName;    end;  end;end;function TJclDebugInfoBinary.GetAddress(const UnitName, ProcName: string): Pointer;var  VA: DWORD;begin  Result := nil;  VA := FScanner.VAFromUnitAndProcName(UnitName, ProcName);  if VA <> 0 then    Result := AddrFromVA(VA);end;function TJclDebugInfoBinary.InitializeSource: Boolean;var  JdbgFileName: TFileName;  VerifyFileName: Boolean;begin  VerifyFileName := False;  Result := (PeMapImgFindSectionFromModule(Pointer(Module), JclDbgDataResName) <> nil);  if Result then    FStream := TJclPeSectionStream.Create(Module, JclDbgDataResName)  else  begin    JdbgFileName := ChangeFileExt(FileName, JclDbgFileExtension);    Result := FileExists(JdbgFileName);    if Result then    begin      FStream := TJclFileMappingStream.Create(JdbgFileName, fmOpenRead or fmShareDenyWrite);      VerifyFileName := True;    end;  end;  if Result then  begin    FScanner := TJclBinDebugScanner.Create(FStream, True, False);    Result := FScanner.ValidFormat and      (not VerifyFileName or FScanner.IsModuleNameValid(FileName));  end;end;//=== { TJclDebugInfoExports } ===============================================destructor TJclDebugInfoExports.Destroy;begin  FreeAndNil(FImage);  inherited Destroy;end;function TJclDebugInfoExports.IsAddressInThisExportedFunction(Addr: PByteArray; FunctionStartAddr: TJclAddr): Boolean;begin  Dec(TJclAddr(Addr), 6);  Result := False;  while TJclAddr(Addr) > FunctionStartAddr do  begin    if IsBadReadPtr(Addr, 6) then      Exit;    if (Addr[0] = $C2) and // ret $xxxx         (((Addr[3] = $90) and (Addr[4] = $90) and (Addr[5] = $90)) or // nop          ((Addr[3] = $CC) and (Addr[4] = $CC) and (Addr[5] = $CC))) then // int 3      Exit;    if (Addr[0] = $C3) and // ret         (((Addr[1] = $90) and (Addr[2] = $90) and (Addr[3] = $90)) or // nop          ((Addr[1] = $CC) and (Addr[2] = $CC) and (Addr[3] = $CC))) then // int 3      Exit;    if (Addr[0] = $E9) and // jmp rel-far         (((Addr[5] = $90) and (Addr[6] = $90) and (Addr[7] = $90)) or // nop          ((Addr[5] = $CC) and (Addr[6] = $CC) and (Addr[7] = $CC))) then // int 3      Exit;    if (Addr[0] = $EB) and // jmp rel-near         (((Addr[2] = $90) and (Addr[3] = $90) and (Addr[4] = $90)) or // nop          ((Addr[2] = $CC) and (Addr[3] = $CC) and (Addr[4] = $CC))) then // int 3      Exit;    Dec(TJclAddr(Addr));  end;  Result := True;end;function TJclDebugInfoExports.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;var  I, BasePos: Integer;  VA: DWORD;  Desc: TJclBorUmDescription;  Unmangled: string;  RawName: Boolean;begin  Result := False;  VA := DWORD(TJclAddr(Addr) - TJclAddr(FModule));  {$IFDEF BORLAND}  RawName := not FImage.IsPackage;  {$ENDIF BORLAND}  {$IFDEF FPC}  RawName := True;  {$ENDIF FPC}  Info.OffsetFromProcName := 0;  Info.OffsetFromLineNumber := 0;  Info.BinaryFileName := FileName;  with FImage.ExportList do  begin    SortList(esAddress, False);    for I := Count - 1 downto 0 do      if Items[I].Address <= VA then      begin        if RawName then        begin          Info.ProcedureName := Items[I].Name;          Info.OffsetFromProcName := VA - Items[I].Address;          Result := True;        end        else        begin          case PeBorUnmangleName(Items[I].Name, Unmangled, Desc, BasePos) of            urOk:              begin                Info.UnitName := Copy(Unmangled, 1, BasePos - 2);                if not (Desc.Kind in [skRTTI, skVTable]) then                begin                  Info.ProcedureName := Copy(Unmangled, BasePos, Length(Unmangled));                  if smLinkProc in Desc.Modifiers then                    Info.ProcedureName := '@' + Info.ProcedureName;                  Info.OffsetFromProcName := VA - Items[I].Address;                end;                Result := True;              end;            urNotMangled:              begin                Info.ProcedureName := Items[I].Name;                Info.OffsetFromProcName := VA - Items[I].Address;                Result := True;              end;          end;        end;        if Result then        begin          Info.Address := Addr;          Info.DebugInfo := Self;          { Check if we have a valid address in an exported function. }          if not IsAddressInThisExportedFunction(Addr, FModule + Items[I].Address) then          begin            //Info.UnitName := '[' + AnsiLowerCase(ExtractFileName(GetModulePath(FModule))) + ']'            {$IFNDEF WINSCP}            Info.ProcedureName := Format(LoadResString(@RsUnknownFunctionAt), [Info.ProcedureName]);            {$ELSE}            Info.ProcedureName := '';            {$ENDIF ~WINSCP}          end;          Break;        end;      end;  end;end;function TJclDebugInfoExports.GetAddress(const UnitName, ProcName: string): Pointer;var  I, BasePos: Integer;  Desc: TJclBorUmDescription;  RawName: Boolean;  ItemUnitName: string;  Unmangled: string;begin  Result := nil;  {$IFDEF BORLAND}  RawName := not FImage.IsPackage;  {$ENDIF BORLAND}  {$IFDEF FPC}  RawName := True;  {$ENDIF FPC}  with FImage.ExportList do  begin//    SortList(esAddress, False);    for I := 0 to Count - 1 do    begin      if RawName then      begin        ItemUnitName := '';        Unmangled := Items[I].Name;      end      else      begin        case PeBorUnmangleName(Items[I].Name, Unmangled, Desc, BasePos) of          urOk:            begin              ItemUnitName := Copy(Unmangled, 1, BasePos - 2);              if not (Desc.Kind in [skRTTI, skVTable]) then              begin                Unmangled := Copy(Unmangled, BasePos, Length(Unmangled));                if smLinkProc in Desc.Modifiers then                  Unmangled := '@' + Unmangled;              end;            end;          urNotMangled:            Unmangled := Items[I].Name;        end;      end;      if ((ItemUnitName = '') or (CompareStr(ItemUnitName, UnitName) = 0)) and (CompareStr(Unmangled, ProcName) = 0) then      begin        Result := AddrFromVA(Items[I].Address);        Break;      end;    end;  end;end;function TJclDebugInfoExports.InitializeSource: Boolean;begin  {$IFDEF BORLAND}  FImage := TJclPeBorImage.Create(True);  {$ENDIF BORLAND}  {$IFDEF FPC}  FImage := TJclPeImage.Create(True);  {$ENDIF FPC}  FImage.AttachLoadedModule(FModule);  Result := FImage.StatusOK and (FImage.ExportList.Count > 0);end;{$IFDEF BORLAND}{$IFNDEF WINSCP}//=== { TJclDebugInfoTD32 } ==================================================destructor TJclDebugInfoTD32.Destroy;begin  FreeAndNil(FImage);  inherited Destroy;end;function TJclDebugInfoTD32.GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;var  VA: DWORD;begin  VA := VAFromAddr(Addr);  Info.UnitName := FImage.TD32Scanner.ModuleNameFromAddr(VA);  Result := Info.UnitName <> '';  if Result then    with Info do    begin      Address := Addr;      ProcedureName := FImage.TD32Scanner.ProcNameFromAddr(VA, OffsetFromProcName);      LineNumber := FImage.TD32Scanner.LineNumberFromAddr(VA, OffsetFromLineNumber);      SourceName := FImage.TD32Scanner.SourceNameFromAddr(VA);      DebugInfo := Self;      BinaryFileName := FileName;    end;end;function TJclDebugInfoTD32.GetAddress(const UnitName, ProcName: string): Pointer;var  VA: DWORD;begin  Result := nil;  VA := FImage.TD32Scanner.VAFromUnitAndProcName(UnitName, ProcName);  if VA <> 0 then    Result := AddrFromVA(VA);end;function TJclDebugInfoTD32.InitializeSource: Boolean;begin  FImage := TJclPeBorTD32Image.Create(True);  try    FImage.AttachLoadedModule(Module);    Result := FImage.IsTD32DebugPresent;  except    Result := False;  end;end;procedure TJclDebugInfoTD32.GenerateUnmangledNames;begin  FImage.TD32Scanner.GenerateUnmangledNames;end;{$ENDIF ~WINSCP}{$ENDIF BORLAND}//=== { TJclDebugInfoSymbols } ===============================================type  TSymInitializeAFunc = function (hProcess: THandle; UserSearchPath: LPSTR;    fInvadeProcess: Bool): Bool; stdcall;  TSymInitializeWFunc = function (hProcess: THandle; UserSearchPath: LPWSTR;    fInvadeProcess: Bool): Bool; stdcall;  TSymGetOptionsFunc = function: DWORD; stdcall;  TSymSetOptionsFunc = function (SymOptions: DWORD): DWORD; stdcall;  TSymCleanupFunc = function (hProcess: THandle): Bool; stdcall;  {$IFDEF CPU32}  TSymGetSymFromAddrAFunc = function (hProcess: THandle; dwAddr: DWORD;    pdwDisplacement: PDWORD; var Symbol: JclWin32.TImagehlpSymbolA): Bool; stdcall;  TSymGetSymFromAddrWFunc = function (hProcess: THandle; dwAddr: DWORD;    pdwDisplacement: PDWORD; var Symbol: JclWin32.TImagehlpSymbolW): Bool; stdcall;  TSymGetModuleInfoAFunc = function (hProcess: THandle; dwAddr: DWORD;    var ModuleInfo: JclWin32.TImagehlpModuleA): Bool; stdcall;  TSymGetModuleInfoWFunc = function (hProcess: THandle; dwAddr: DWORD;    var ModuleInfo: JclWin32.TImagehlpModuleW): Bool; stdcall;  TSymLoadModuleFunc = function (hProcess: THandle; hFile: THandle; ImageName,    ModuleName: LPSTR; BaseOfDll: DWORD; SizeOfDll: DWORD): DWORD; stdcall;  TSymGetLineFromAddrAFunc = function (hProcess: THandle; dwAddr: DWORD;    pdwDisplacement: PDWORD; var Line: JclWin32.TImageHlpLineA): Bool; stdcall;  TSymGetLineFromAddrWFunc = function (hProcess: THandle; dwAddr: DWORD;    pdwDisplacement: PDWORD; var Line: JclWin32.TImageHlpLineW): Bool; stdcall;  {$ENDIF CPU32}  {$IFDEF CPU64}  TSymGetSymFromAddrAFunc = function (hProcess: THandle; dwAddr: DWORD64;    pdwDisplacement: PDWORD64; var Symbol: JclWin32.TImagehlpSymbolA64): Bool; stdcall;  TSymGetSymFromAddrWFunc = function (hProcess: THandle; dwAddr: DWORD64;    pdwDisplacement: PDWORD64; var Symbol: JclWin32.TImagehlpSymbolW64): Bool; stdcall;  TSymGetModuleInfoAFunc = function (hProcess: THandle; dwAddr: DWORD64;    var ModuleInfo: JclWin32.TImagehlpModuleA64): Bool; stdcall;  TSymGetModuleInfoWFunc = function (hProcess: THandle; dwAddr: DWORD64;    var ModuleInfo: JclWin32.TImagehlpModuleW64): Bool; stdcall;  TSymLoadModuleFunc = function (hProcess: THandle; hFile: THandle; ImageName,    ModuleName: LPSTR; BaseOfDll: DWORD64; SizeOfDll: DWORD): DWORD; stdcall;  TSymGetLineFromAddrAFunc = function (hProcess: THandle; dwAddr: DWORD64;    pdwDisplacement: PDWORD; var Line: JclWin32.TImageHlpLineA64): Bool; stdcall;  TSymGetLineFromAddrWFunc = function (hProcess: THandle; dwAddr: DWORD64;    pdwDisplacement: PDWORD; var Line: JclWin32.TImageHlpLineW64): Bool; stdcall;  {$ENDIF CPU64}var  DebugSymbolsInitialized: Boolean = False;  DebugSymbolsLoadFailed: Boolean = False;  ImageHlpDllHandle: THandle = 0;  SymInitializeAFunc: TSymInitializeAFunc = nil;  SymInitializeWFunc: TSymInitializeWFunc = nil;  SymGetOptionsFunc: TSymGetOptionsFunc = nil;  SymSetOptionsFunc: TSymSetOptionsFunc = nil;  SymCleanupFunc: TSymCleanupFunc = nil;  SymGetSymFromAddrAFunc: TSymGetSymFromAddrAFunc = nil;  SymGetSymFromAddrWFunc: TSymGetSymFromAddrWFunc = nil;  SymGetModuleInfoAFunc: TSymGetModuleInfoAFunc = nil;  SymGetModuleInfoWFunc: TSymGetModuleInfoWFunc = nil;  SymLoadModuleFunc: TSymLoadModuleFunc = nil;  SymGetLineFromAddrAFunc: TSymGetLineFromAddrAFunc = nil;  SymGetLineFromAddrWFunc: TSymGetLineFromAddrWFunc = nil;const  ImageHlpDllName = 'imagehlp.dll';                          // do not localize  SymInitializeAFuncName = 'SymInitialize';                  // do not localize  SymInitializeWFuncName = 'SymInitializeW';                 // do not localize  SymGetOptionsFuncName = 'SymGetOptions';                   // do not localize  SymSetOptionsFuncName = 'SymSetOptions';                   // do not localize  SymCleanupFuncName = 'SymCleanup';                         // do not localize  {$IFDEF CPU32}  SymGetSymFromAddrAFuncName = 'SymGetSymFromAddr';          // do not localize  SymGetSymFromAddrWFuncName = 'SymGetSymFromAddrW';         // do not localize  SymGetModuleInfoAFuncName = 'SymGetModuleInfo';            // do not localize  SymGetModuleInfoWFuncName = 'SymGetModuleInfoW';           // do not localize  SymLoadModuleFuncName = 'SymLoadModule';                   // do not localize  SymGetLineFromAddrAFuncName = 'SymGetLineFromAddr';        // do not localize  SymGetLineFromAddrWFuncName = 'SymGetLineFromAddrW';       // do not localize  {$ENDIF CPU32}  {$IFDEF CPU64}  SymGetSymFromAddrAFuncName = 'SymGetSymFromAddr64';        // do not localize  SymGetSymFromAddrWFuncName = 'SymGetSymFromAddrW64';       // do not localize  SymGetModuleInfoAFuncName = 'SymGetModuleInfo64';          // do not localize  SymGetModuleInfoWFuncName = 'SymGetModuleInfoW64';         // do not localize  SymLoadModuleFuncName = 'SymLoadModule64';                 // do not localize  SymGetLineFromAddrAFuncName = 'SymGetLineFromAddr64';      // do not localize  SymGetLineFromAddrWFuncName = 'SymGetLineFromAddrW64';     // do not localize  {$ENDIF CPU64}function StrRemoveEmptyPaths(const Paths: string): string;var  List: TStrings;  I: Integer;begin  List := TStringList.Create;  try    StrToStrings(Paths, DirSeparator, List, False);    for I := 0 to List.Count - 1 do      if Trim(List[I]) = '' then        List[I] := '';    Result := StringsToStr(List, DirSeparator, False);  finally    List.Free;  end;end;class function TJclDebugInfoSymbols.InitializeDebugSymbols: Boolean;var  EnvironmentVarValue, SearchPath: string;  SymOptions: Cardinal;  ProcessHandle: THandle;begin  Result := DebugSymbolsInitialized;  if not DebugSymbolsLoadFailed then  begin    Result := LoadDebugFunctions;    DebugSymbolsLoadFailed := not Result;    if Result then    begin      if JclDebugInfoSymbolPaths <> '' then      begin        SearchPath := StrEnsureSuffix(DirSeparator, JclDebugInfoSymbolPaths);        SearchPath := StrEnsureNoSuffix(DirSeparator, SearchPath + GetCurrentFolder);        if GetEnvironmentVar(EnvironmentVarNtSymbolPath, EnvironmentVarValue) and (EnvironmentVarValue <> '') then          SearchPath := StrEnsureNoSuffix(DirSeparator, StrEnsureSuffix(DirSeparator, EnvironmentVarValue) + SearchPath);        if GetEnvironmentVar(EnvironmentVarAlternateNtSymbolPath, EnvironmentVarValue) and (EnvironmentVarValue <> '') then          SearchPath := StrEnsureNoSuffix(DirSeparator, StrEnsureSuffix(DirSeparator, EnvironmentVarValue) + SearchPath);        // DbgHelp.dll crashes when an empty path is specified.        // This also means that the SearchPath must not end with a DirSeparator. }        SearchPath := StrRemoveEmptyPaths(SearchPath);      end      else        // Fix crash SymLoadModuleFunc on WinXP SP3 when SearchPath=''        SearchPath := GetCurrentFolder;      if IsWinNT then        // in Windows NT, first argument is a process handle        ProcessHandle := GetCurrentProcess      else        // in Windows 95, 98, ME first argument is a process identifier        ProcessHandle := GetCurrentProcessId;      // Debug(WinXPSP3): SymInitializeWFunc==nil      if Assigned(SymInitializeWFunc) then        Result := SymInitializeWFunc(ProcessHandle, PWideChar(WideString(SearchPath)), False)      else      if Assigned(SymInitializeAFunc) then        Result := SymInitializeAFunc(ProcessHandle, PAnsiChar(AnsiString(SearchPath)), False)      else        Result := False;      if Result then      begin        SymOptions := SymGetOptionsFunc or SYMOPT_DEFERRED_LOADS          or SYMOPT_FAIL_CRITICAL_ERRORS or SYMOPT_INCLUDE_32BIT_MODULES or SYMOPT_LOAD_LINES;        SymOptions := SymOptions and (not (SYMOPT_NO_UNQUALIFIED_LOADS or SYMOPT_UNDNAME));        SymSetOptionsFunc(SymOptions);      end;      DebugSymbolsInitialized := Result;    end    else      UnloadDebugFunctions;  end;end;class function TJclDebugInfoSymbols.CleanupDebugSymbols: Boolean;begin  Result := True;  if DebugSymbolsInitialized then    Result := SymCleanupFunc(GetCurrentProcess);  UnloadDebugFunctions;end;function TJclDebugInfoSymbols.GetLocationInfo(const Addr: Pointer;  out Info: TJclLocationInfo): Boolean;const  SymbolNameLength = 1000;  {$IFDEF CPU32}  SymbolSizeA = SizeOf(TImagehlpSymbolA) + SymbolNameLength * SizeOf(AnsiChar);  SymbolSizeW = SizeOf(TImagehlpSymbolW) + SymbolNameLength * SizeOf(WideChar);  {$ENDIF CPU32}  {$IFDEF CPU64}  SymbolSizeA = SizeOf(TImagehlpSymbolA64) + SymbolNameLength * SizeOf(AnsiChar);  SymbolSizeW = SizeOf(TImagehlpSymbolW64) + SymbolNameLength * SizeOf(WideChar);  {$ENDIF CPU64}var  Displacement: DWORD;  ProcessHandle: THandle;  {$IFDEF CPU32}  SymbolA: PImagehlpSymbolA;  SymbolW: PImagehlpSymbolW;  LineA: TImageHlpLineA;  LineW: TImageHlpLineW;  {$ENDIF CPU32}  {$IFDEF CPU64}  SymbolA: PImagehlpSymbolA64;  SymbolW: PImagehlpSymbolW64;  LineA: TImageHlpLineA64;  LineW: TImageHlpLineW64;  {$ENDIF CPU64}begin  ProcessHandle := GetCurrentProcess;  if Assigned(SymGetSymFromAddrWFunc) then  begin    GetMem(SymbolW, SymbolSizeW);    try      ZeroMemory(SymbolW, SymbolSizeW);      SymbolW^.SizeOfStruct := SizeOf(SymbolW^);      SymbolW^.MaxNameLength := SymbolNameLength;      Displacement := 0;      Result := SymGetSymFromAddrWFunc(ProcessHandle, TJclAddr(Addr), @Displacement, SymbolW^);      if Result then      begin        Info.DebugInfo := Self;        Info.Address := Addr;        Info.BinaryFileName := FileName;        Info.OffsetFromProcName := Displacement;        JclPeImage.UnDecorateSymbolName(string(PWideChar(@SymbolW^.Name[0])), Info.ProcedureName, UNDNAME_NAME_ONLY or UNDNAME_NO_ARGUMENTS);      end;    finally      FreeMem(SymbolW);    end;  end  else  if Assigned(SymGetSymFromAddrAFunc) then  begin    GetMem(SymbolA, SymbolSizeA);    try      ZeroMemory(SymbolA, SymbolSizeA);      SymbolA^.SizeOfStruct := SizeOf(SymbolA^);      SymbolA^.MaxNameLength := SymbolNameLength;      Displacement := 0;      Result := SymGetSymFromAddrAFunc(ProcessHandle, TJclAddr(Addr), @Displacement, SymbolA^);      if Result then      begin        Info.DebugInfo := Self;        Info.Address := Addr;        Info.BinaryFileName := FileName;        Info.OffsetFromProcName := Displacement;        JclPeImage.UnDecorateSymbolName(string(PAnsiChar(@SymbolA^.Name[0])), Info.ProcedureName, UNDNAME_NAME_ONLY or UNDNAME_NO_ARGUMENTS);      end;    finally      FreeMem(SymbolA);    end;  end  else    Result := False;  // line number is optional  if Result and Assigned(SymGetLineFromAddrWFunc) then  begin    ZeroMemory(@LineW, SizeOf(LineW));    LineW.SizeOfStruct := SizeOf(LineW);    Displacement := 0;    if SymGetLineFromAddrWFunc(ProcessHandle, TJclAddr(Addr), @Displacement, LineW) then    begin      Info.LineNumber := LineW.LineNumber;      Info.UnitName := string(LineW.FileName);      Info.OffsetFromLineNumber := Displacement;    end;  end  else  if Result and Assigned(SymGetLineFromAddrAFunc) then  begin    ZeroMemory(@LineA, SizeOf(LineA));    LineA.SizeOfStruct := SizeOf(LineA);    Displacement := 0;    if SymGetLineFromAddrAFunc(ProcessHandle, TJclAddr(Addr), @Displacement, LineA) then    begin      Info.LineNumber := LineA.LineNumber;      Info.UnitName := string(LineA.FileName);      Info.OffsetFromLineNumber := Displacement;    end;  end;end;function TJclDebugInfoSymbols.GetAddress(const UnitName, ProcName: string): Pointer;var  VA: DWORD;begin  Result := nil;  VA := 0; // FScanner.VAFromUnitAndProcName(UnitName, ProcName);  if VA <> 0 then    Result := AddrFromVA(VA);end;function TJclDebugInfoSymbols.InitializeSource: Boolean;var  ModuleFileName: TFileName;  {$IFDEF CPU32}  ModuleInfoA: TImagehlpModuleA;  ModuleInfoW: TImagehlpModuleW;  {$ENDIF CPU32}  {$IFDEF CPU64}  ModuleInfoA: TImagehlpModuleA64;  ModuleInfoW: TImagehlpModuleW64;  {$ENDIF CPU64}  ProcessHandle: THandle;begin  Result := InitializeDebugSymbols;  if Result then  begin    if IsWinNT then      // in Windows NT, first argument is a process handle      ProcessHandle := GetCurrentProcess    else      // in Windows 95, 98, ME, first argument is a process identifier      ProcessHandle := GetCurrentProcessId;    if Assigned(SymGetModuleInfoWFunc) then    begin      ZeroMemory(@ModuleInfoW, SizeOf(ModuleInfoW));      ModuleInfoW.SizeOfStruct := SizeOf(ModuleInfoW);      Result := SymGetModuleInfoWFunc(ProcessHandle, Module, ModuleInfoW);      if not Result then      begin        // the symbols for this module are not loaded yet: load the module and query for the symbol again        ModuleFileName := GetModulePath(Module);        ZeroMemory(@ModuleInfoW, SizeOf(ModuleInfoW));        ModuleInfoW.SizeOfStruct := SizeOf(ModuleInfoW);        // warning: crash on WinXP SP3 when SymInitializeAFunc is called with empty SearchPath        // OF: possible loss of data        Result := (SymLoadModuleFunc(ProcessHandle, 0, PAnsiChar(AnsiString(ModuleFileName)), nil, 0, 0) <> 0) and                  SymGetModuleInfoWFunc(ProcessHandle, Module, ModuleInfoW);      end;      Result := Result and (ModuleInfoW.BaseOfImage <> 0) and                not (ModuleInfoW.SymType in [SymNone, SymExport]);    end    else    if Assigned(SymGetModuleInfoAFunc) then    begin      ZeroMemory(@ModuleInfoA, SizeOf(ModuleInfoA));      ModuleInfoA.SizeOfStruct := SizeOf(ModuleInfoA);      Result := SymGetModuleInfoAFunc(ProcessHandle, Module, ModuleInfoA);      if not Result then      begin        // the symbols for this module are not loaded yet: load the module and query for the symbol again        ModuleFileName := GetModulePath(Module);        ZeroMemory(@ModuleInfoA, SizeOf(ModuleInfoA));        ModuleInfoA.SizeOfStruct := SizeOf(ModuleInfoA);        // warning: crash on WinXP SP3 when SymInitializeAFunc is called with empty SearchPath        // OF: possible loss of data        Result := (SymLoadModuleFunc(ProcessHandle, 0, PAnsiChar(AnsiString(ModuleFileName)), nil, 0, 0) <> 0) and                  SymGetModuleInfoAFunc(ProcessHandle, Module, ModuleInfoA);      end;      Result := Result and (ModuleInfoA.BaseOfImage <> 0) and                not (ModuleInfoA.SymType in [SymNone, SymExport]);    end    else      Result := False;  end;end;class function TJclDebugInfoSymbols.LoadDebugFunctions: Boolean;begin  ImageHlpDllHandle := SafeLoadLibrary(ImageHlpDllName);  if ImageHlpDllHandle <> 0 then  begin    SymInitializeAFunc := GetProcAddress(ImageHlpDllHandle, SymInitializeAFuncName);    SymInitializeWFunc := GetProcAddress(ImageHlpDllHandle, SymInitializeWFuncName);    SymGetOptionsFunc := GetProcAddress(ImageHlpDllHandle, SymGetOptionsFuncName);    SymSetOptionsFunc := GetProcAddress(ImageHlpDllHandle, SymSetOptionsFuncName);    SymCleanupFunc := GetProcAddress(ImageHlpDllHandle, SymCleanupFuncName);    SymGetSymFromAddrAFunc := GetProcAddress(ImageHlpDllHandle, SymGetSymFromAddrAFuncName);    SymGetSymFromAddrWFunc := GetProcAddress(ImageHlpDllHandle, SymGetSymFromAddrWFuncName);    SymGetModuleInfoAFunc := GetProcAddress(ImageHlpDllHandle, SymGetModuleInfoAFuncName);    SymGetModuleInfoWFunc := GetProcAddress(ImageHlpDllHandle, SymGetModuleInfoWFuncName);    SymLoadModuleFunc := GetProcAddress(ImageHlpDllHandle, SymLoadModuleFuncName);    SymGetLineFromAddrAFunc := GetProcAddress(ImageHlpDllHandle, SymGetLineFromAddrAFuncName);    SymGetLineFromAddrWFunc := GetProcAddress(ImageHlpDllHandle, SymGetLineFromAddrWFuncName);  end;  // SymGetLineFromAddrFunc is optional  Result := (ImageHlpDllHandle <> 0) and    Assigned(SymGetOptionsFunc) and Assigned(SymSetOptionsFunc) and    Assigned(SymCleanupFunc) and Assigned(SymLoadModuleFunc) and    (Assigned(SymInitializeAFunc) or Assigned(SymInitializeWFunc)) and    (Assigned(SymGetSymFromAddrAFunc) or Assigned(SymGetSymFromAddrWFunc)) and    (Assigned(SymGetModuleInfoAFunc) or Assigned(SymGetModuleInfoWFunc));end;class function TJclDebugInfoSymbols.UnloadDebugFunctions: Boolean;begin  Result := ImageHlpDllHandle <> 0;  if Result then    FreeLibrary(ImageHlpDllHandle);  ImageHlpDllHandle := 0;  SymInitializeAFunc := nil;  SymInitializeWFunc := nil;  SymGetOptionsFunc := nil;  SymSetOptionsFunc := nil;  SymCleanupFunc := nil;  SymGetSymFromAddrAFunc := nil;  SymGetSymFromAddrWFunc := nil;  SymGetModuleInfoAFunc := nil;  SymGetModuleInfoWFunc := nil;  SymLoadModuleFunc := nil;  SymGetLineFromAddrAFunc := nil;  SymGetLineFromAddrWFunc := nil;end;//=== Source location functions =============================================={$STACKFRAMES ON}function Caller(Level: Integer; FastStackWalk: Boolean): Pointer;var  TopOfStack: TJclAddr;  BaseOfStack: TJclAddr;  StackFrame: PStackFrame;begin  Result := nil;  try    if FastStackWalk then    begin      StackFrame := GetFramePointer;      BaseOfStack := TJclAddr(StackFrame) - 1;      TopOfStack := GetStackTop;      while (BaseOfStack < TJclAddr(StackFrame)) and (TJclAddr(StackFrame) < TopOfStack) do      begin        if Level = 0 then        begin          Result := Pointer(StackFrame^.CallerAddr - 1);          Break;        end;        StackFrame := PStackFrame(StackFrame^.CallerFrame);        Dec(Level);      end;    end    else    with TJclStackInfoList.Create(False, 1, nil, False, nil, nil) do    try      if Level < Count then        Result := Items[Level].CallerAddr;    finally      Free;    end;  except    Result := nil;  end;end;{$IFNDEF STACKFRAMES_ON}{$STACKFRAMES OFF}{$ENDIF ~STACKFRAMES_ON}procedure BeginGetLocationInfoCache;begin  BeginModuleFromAddrCache;end;procedure EndGetLocationInfoCache;begin  EndModuleFromAddrCache;end;function GetLocationInfo(const Addr: Pointer): TJclLocationInfo;begin  try    DebugInfoCritSect.Enter;    try      NeedDebugInfoList;      DebugInfoList.GetLocationInfo(Addr, Result)    finally      DebugInfoCritSect.Leave;    end;  except    Finalize(Result);    ResetMemory(Result, SizeOf(Result));  end;end;function GetLocationInfo(const Addr: Pointer; out Info: TJclLocationInfo): Boolean;begin  try    DebugInfoCritSect.Enter;    try      NeedDebugInfoList;      Result := DebugInfoList.GetLocationInfo(Addr, Info);    finally      DebugInfoCritSect.Leave;    end;  except    Result := False;  end;end;function GetLocationInfoStr(const Addr: Pointer; IncludeModuleName, IncludeAddressOffset,  IncludeStartProcLineOffset: Boolean; IncludeVAddress: Boolean): string;var  Info, StartProcInfo: TJclLocationInfo;  OffsetStr, StartProcOffsetStr, FixedProcedureName, UnitNameWithoutUnitscope: string;  Module: HMODULE;  {$IFDEF WINSCP}  MainModule: HMODULE;  ModuleName: string;  ModulePosition: Integer;  {$ENDIF ~WINSCP}begin  OffsetStr := '';  if GetLocationInfo(Addr, Info) then  with Info do  begin    FixedProcedureName := ProcedureName;    if Pos(UnitName + '.', FixedProcedureName) = 1 then      FixedProcedureName := Copy(FixedProcedureName, Length(UnitName) + 2, Length(FixedProcedureName) - Length(UnitName) - 1)    else    if Pos('.', UnitName) > 1 then    begin      UnitNameWithoutUnitscope := UnitName;      Delete(UnitNameWithoutUnitscope, 1, Pos('.', UnitNameWithoutUnitscope));      if Pos(StrLower(UnitNameWithoutUnitscope) + '.', StrLower(FixedProcedureName)) = 1 then        FixedProcedureName := Copy(FixedProcedureName, Length(UnitNameWithoutUnitscope) + 2, Length(FixedProcedureName) - Length(UnitNameWithoutUnitscope) - 1);    end;    if LineNumber > 0 then    begin      if IncludeStartProcLineOffset and GetLocationInfo(Pointer(TJclAddr(Info.Address) -        Cardinal(Info.OffsetFromProcName)), StartProcInfo) and (StartProcInfo.LineNumber > 0) then          StartProcOffsetStr := Format(' + %d', [LineNumber - StartProcInfo.LineNumber])      else        StartProcOffsetStr := '';      if IncludeAddressOffset then      begin        if OffsetFromLineNumber >= 0 then          OffsetStr := Format(' + $%x', [OffsetFromLineNumber])        else          OffsetStr := Format(' - $%x', [-OffsetFromLineNumber])      end;      {$IFDEF WINSCP}      Result := Format('[%p] %s (Line %u, "%s"%s)%s', [Addr, FixedProcedureName, LineNumber,        SourceName, StartProcOffsetStr, OffsetStr]);      {$ELSE}      Result := Format('[%p] %s.%s (Line %u, "%s"%s)%s', [Addr, UnitName, FixedProcedureName, LineNumber,        SourceName, StartProcOffsetStr, OffsetStr]);      {$ENDIF}    end    else    begin      if IncludeAddressOffset then        OffsetStr := Format(' + $%x', [OffsetFromProcName]);      {$IFNDEF WINSCP}      if UnitName <> '' then        Result := Format('[%p] %s.%s%s', [Addr, UnitName, FixedProcedureName, OffsetStr])      else      {$ENDIF}        Result := Format('[%p] %s%s', [Addr, FixedProcedureName, OffsetStr]);    end;  end  else  begin    Result := Format('[%p]', [Addr]);    IncludeVAddress := True;  end;  if IncludeVAddress or IncludeModuleName then  begin    Module := ModuleFromAddr(Addr);    if IncludeVAddress then    begin{$OVERFLOWCHECKS OFF} // Mantis #6104      OffsetStr := Format('(%p) ', [Pointer(TJclAddr(Addr) - TJclAddr(Module) - ModuleCodeOffset)]);{$IFDEF OVERFLOWCHECKS_ON}{$OVERFLOWCHECKS ON}{$ENDIF OVERFLOWCHECKS_OFF}      Result := OffsetStr + Result;    end;    if IncludeModuleName then    {$IFDEF WINSCP}    begin      MainModule := GetModuleHandle(nil);      if MainModule <> Module then      begin        ModuleName := ExtractFileName(GetModulePath(Module));        ModulePosition := 12 {$IFDEF CPU64}+8{$ENDIF};        if IncludeVAddress then          ModulePosition := 2 * (ModulePosition - 1) + 1;        if ModulePosition < Length(Result) then          ModuleName := ModuleName + '.';        Insert(ModuleName, Result, ModulePosition);      end;    end;    {$ELSE}      Insert(Format('{%-12s}', [ExtractFileName(GetModulePath(Module))]), Result, 11 {$IFDEF CPU64}+8{$ENDIF});    {$ENDIF ~WINSCP}  end;end;function DebugInfoAvailable(const Module: HMODULE): Boolean;begin  DebugInfoCritSect.Enter;  try    NeedDebugInfoList;    Result := (DebugInfoList.ItemFromModule[Module] <> nil);  finally    DebugInfoCritSect.Leave;  end;end;procedure ClearLocationData;begin  DebugInfoCritSect.Enter;  try    if DebugInfoList <> nil then      DebugInfoList.Clear;  finally    DebugInfoCritSect.Leave;  end;end;{$STACKFRAMES ON}function FileByLevel(const Level: Integer): string;begin  Result := GetLocationInfo(Caller(Level + 1)).SourceName;end;function ModuleByLevel(const Level: Integer): string;begin  Result := GetLocationInfo(Caller(Level + 1)).UnitName;end;function ProcByLevel(const Level: Integer; OnlyProcedureName: boolean): string;begin  Result := GetLocationInfo(Caller(Level + 1)).ProcedureName;  if OnlyProcedureName = true then  begin    if StrILastPos('.', Result) > 0 then      Result :=StrRestOf(Result, StrILastPos('.', Result)+1);  end;end;function LineByLevel(const Level: Integer): Integer;begin  Result := GetLocationInfo(Caller(Level + 1)).LineNumber;end;function MapByLevel(const Level: Integer; var File_, Module_, Proc_: string;  var Line_: Integer): Boolean;begin  Result := MapOfAddr(Caller(Level + 1), File_, Module_, Proc_, Line_);end;function ExtractClassName(const ProcedureName: string): string;var  D: Integer;begin  D := Pos('.', ProcedureName);  if D < 2 then    Result := ''  else    Result := Copy(ProcedureName, 1, D - 1);end;function ExtractMethodName(const ProcedureName: string): string;begin  Result := Copy(ProcedureName, Pos('.', ProcedureName) + 1, Length(ProcedureName));end;function __FILE__(const Level: Integer): string;begin  Result := FileByLevel(Level + 1);end;function __MODULE__(const Level: Integer): string;begin  Result := ModuleByLevel(Level + 1);end;function __PROC__(const Level: Integer): string;begin  Result := ProcByLevel(Level + 1);end;function __LINE__(const Level: Integer): Integer;begin  Result := LineByLevel(Level + 1);end;function __MAP__(const Level: Integer; var _File, _Module, _Proc: string; var _Line: Integer): Boolean;begin  Result := MapByLevel(Level + 1, _File, _Module, _Proc, _Line);end;{$IFNDEF STACKFRAMES_ON}{$STACKFRAMES OFF}{$ENDIF ~STACKFRAMES_ON}function FileOfAddr(const Addr: Pointer): string;begin  Result := GetLocationInfo(Addr).SourceName;end;function ModuleOfAddr(const Addr: Pointer): string;begin  Result := GetLocationInfo(Addr).UnitName;end;function ProcOfAddr(const Addr: Pointer): string;begin  Result := GetLocationInfo(Addr).ProcedureName;end;function LineOfAddr(const Addr: Pointer): Integer;begin  Result := GetLocationInfo(Addr).LineNumber;end;function MapOfAddr(const Addr: Pointer; var File_, Module_, Proc_: string;  var Line_: Integer): Boolean;var  LocInfo: TJclLocationInfo;begin  NeedDebugInfoList;  Result := DebugInfoList.GetLocationInfo(Addr, LocInfo);  if Result then  begin    File_ := LocInfo.SourceName;    Module_ := LocInfo.UnitName;    Proc_ := LocInfo.ProcedureName;    Line_ := LocInfo.LineNumber;  end;end;function __FILE_OF_ADDR__(const Addr: Pointer): string;begin  Result := FileOfAddr(Addr);end;function __MODULE_OF_ADDR__(const Addr: Pointer): string;begin  Result := ModuleOfAddr(Addr);end;function __PROC_OF_ADDR__(const Addr: Pointer): string;begin  Result := ProcOfAddr(Addr);end;function __LINE_OF_ADDR__(const Addr: Pointer): Integer;begin  Result := LineOfAddr(Addr);end;function __MAP_OF_ADDR__(const Addr: Pointer; var _File, _Module, _Proc: string;  var _Line: Integer): Boolean;begin  Result := MapOfAddr(Addr, _File, _Module, _Proc, _Line);end;//=== { TJclStackBaseList } ==================================================constructor TJclStackBaseList.Create;begin  inherited Create(True);  FThreadID := GetCurrentThreadId;  FTimeStamp := Now;end;destructor TJclStackBaseList.Destroy;begin  if Assigned(FOnDestroy) then    FOnDestroy(Self);  inherited Destroy;end;//=== { TJclGlobalStackList } ================================================type  TJclStackBaseListClass = class of TJclStackBaseList;  TJclGlobalStackList = class(TThreadList)  private    FLockedTID: DWORD;    FTIDLocked: Boolean;    function GetExceptStackInfo(TID: DWORD): TJclStackInfoList;    function GetLastExceptFrameList(TID: DWORD): TJclExceptFrameList;    procedure ItemDestroyed(Sender: TObject);  public    destructor Destroy; override;    procedure AddObject(AObject: TJclStackBaseList);    procedure Clear;    procedure LockThreadID(TID: DWORD);    procedure UnlockThreadID;    function FindObject(TID: DWORD; AClass: TJclStackBaseListClass): TJclStackBaseList;    property ExceptStackInfo[TID: DWORD]: TJclStackInfoList read GetExceptStackInfo;    property LastExceptFrameList[TID: DWORD]: TJclExceptFrameList read GetLastExceptFrameList;  end;var  GlobalStackList: TJclGlobalStackList;destructor TJclGlobalStackList.Destroy;begin  with LockList do  try    while Count > 0 do      TObject(Items[0]).Free;  finally    UnlockList;  end;  inherited Destroy;end;procedure TJclGlobalStackList.AddObject(AObject: TJclStackBaseList);var  ReplacedObj: TObject;begin  AObject.FOnDestroy := ItemDestroyed;  with LockList do  try    ReplacedObj := FindObject(AObject.ThreadID, TJclStackBaseListClass(AObject.ClassType));    if ReplacedObj <> nil then    begin      Remove(ReplacedObj);      ReplacedObj.Free;    end;    Add(AObject);  finally    UnlockList;  end;end;procedure TJclGlobalStackList.Clear;begin  with LockList do  try    while Count > 0 do      TObject(Items[0]).Free;    { The following call to Clear seems to be useless, but it deallocates memory      by setting the lists capacity back to zero. For the runtime memory leak check      within DUnit it is important that the allocated memory before and after the      test is equal. }    Clear; // do not remove  finally    UnlockList;  end;end;function TJclGlobalStackList.FindObject(TID: DWORD; AClass: TJclStackBaseListClass): TJclStackBaseList;var  I: Integer;  Item: TJclStackBaseList;begin  Result := nil;  with LockList do  try    if FTIDLocked and (GetCurrentThreadId = MainThreadID) then      TID := FLockedTID;    for I := 0 to Count - 1 do    begin      Item := Items[I];      if (Item.ThreadID = TID) and (Item is AClass) then      begin        Result := Item;        Break;      end;    end;  finally    UnlockList;  end;end;function TJclGlobalStackList.GetExceptStackInfo(TID: DWORD): TJclStackInfoList;begin  Result := TJclStackInfoList(FindObject(TID, TJclStackInfoList));end;function TJclGlobalStackList.GetLastExceptFrameList(TID: DWORD): TJclExceptFrameList;begin  Result := TJclExceptFrameList(FindObject(TID, TJclExceptFrameList));end;procedure TJclGlobalStackList.ItemDestroyed(Sender: TObject);begin  with LockList do  try    Remove(Sender);  finally    UnlockList;  end;end;procedure TJclGlobalStackList.LockThreadID(TID: DWORD);begin  with LockList do  try    if GetCurrentThreadId = MainThreadID then    begin      FTIDLocked := True;      FLockedTID := TID;    end    else      FTIDLocked := False;  finally    UnlockList;  end;end;procedure TJclGlobalStackList.UnlockThreadID;begin  with LockList do  try    FTIDLocked := False;  finally    UnlockList;  end;end;//=== { TJclGlobalModulesList } ==============================================type  TJclGlobalModulesList = class(TObject)  private    FAddedModules: TStringList;    FHookedModules: TJclModuleArray;    FLock: TJclCriticalSection;    FModulesList: TJclModuleInfoList;  public    constructor Create;    destructor Destroy; override;    procedure AddModule(const ModuleName: string);    function CreateModulesList: TJclModuleInfoList;    procedure FreeModulesList(var ModulesList: TJclModuleInfoList);    function ValidateAddress(Addr: Pointer): Boolean;  end;var  GlobalModulesList: TJclGlobalModulesList;constructor TJclGlobalModulesList.Create;begin  FLock := TJclCriticalSection.Create;end;destructor TJclGlobalModulesList.Destroy;begin  FreeAndNil(FLock);  // Keep FModulesList alive if there are still TJclStackInfoLists referencing it. The  // last JclStackInfoList will destroy it through FreeModulesList.  if (FModulesList <> nil) and (FModulesList.FRefCount = 0) then    FreeAndNil(FModulesList);  FreeAndNil(FAddedModules);  inherited Destroy;end;procedure TJclGlobalModulesList.AddModule(const ModuleName: string);var  IsMultiThreaded: Boolean;begin  IsMultiThreaded := IsMultiThread;  if IsMultiThreaded then    FLock.Enter;  try    if not Assigned(FAddedModules) then    begin      FAddedModules := TStringList.Create;      FAddedModules.Sorted := True;      FAddedModules.Duplicates := dupIgnore;    end;    FAddedModules.Add(ModuleName);  finally    if IsMultiThreaded then      FLock.Leave;  end;end;function TJclGlobalModulesList.CreateModulesList: TJclModuleInfoList;var  I: Integer;  SystemModulesOnly: Boolean;  IsMultiThreaded: Boolean;  AddedModuleHandle: HMODULE;begin  IsMultiThreaded := IsMultiThread;  if IsMultiThreaded then    FLock.Enter;  try    if FModulesList = nil then    begin      SystemModulesOnly := not (stAllModules in JclStackTrackingOptions);      Result := TJclModuleInfoList.Create(False, SystemModulesOnly);      // Add known Borland modules collected by DLL exception hooking code      if SystemModulesOnly and JclHookedExceptModulesList(FHookedModules) then        for I := Low(FHookedModules) to High(FHookedModules) do          Result.AddModule(FHookedModules[I], True);      if Assigned(FAddedModules) then        for I := 0 to FAddedModules.Count - 1 do        begin          AddedModuleHandle := GetModuleHandle(PChar(FAddedModules[I]));          if (AddedModuleHandle <> 0) and            not Assigned(Result.ModuleFromAddress[Pointer(AddedModuleHandle)]) then            Result.AddModule(AddedModuleHandle, True);        end;      if stStaticModuleList in JclStackTrackingOptions then        FModulesList := Result;    end    else      Result := FModulesList;  finally    if IsMultiThreaded then      FLock.Leave;  end;  // RefCount the "global" FModulesList so that if GlobalModulesList is destroyed we can keep  // the FModulesList alive and let it be destroyed by the last TJclStackInfoList.  if Result = FModulesList then    InterlockedIncrement(FModulesList.FRefCount);end;procedure TJclGlobalModulesList.FreeModulesList(var ModulesList: TJclModuleInfoList);var  IsMultiThreaded: Boolean;begin  if Self <> nil then // happens when finalization already ran but a TJclStackInfoList is still alive  begin    if FModulesList <> ModulesList then    begin      IsMultiThreaded := IsMultiThread;      if IsMultiThreaded then        FLock.Enter;      try        FreeAndNil(ModulesList);      finally        if IsMultiThreaded then          FLock.Leave;      end;    end    else if FModulesList <> nil then      InterlockedDecrement(FModulesList.FRefCount);  end  else    if InterlockedDecrement(ModulesList.FRefCount) = 0 then      FreeAndNil(ModulesList);end;function TJclGlobalModulesList.ValidateAddress(Addr: Pointer): Boolean;var  TempList: TJclModuleInfoList;begin  TempList := CreateModulesList;  try    Result := TempList.IsValidModuleAddress(Addr);  finally    FreeModulesList(TempList);  end;end;function JclValidateModuleAddress(Addr: Pointer): Boolean;begin  Result := GlobalModulesList.ValidateAddress(Addr);end;//=== Stack info routines ===================================================={$STACKFRAMES OFF}function ValidCodeAddr(CodeAddr: DWORD; ModuleList: TJclModuleInfoList): Boolean;begin  if stAllModules in JclStackTrackingOptions then    Result := ModuleList.IsValidModuleAddress(Pointer(CodeAddr))  else    Result := ModuleList.IsSystemModuleAddress(Pointer(CodeAddr));end;procedure CorrectExceptStackListTop(List: TJclStackInfoList; SkipFirstItem: Boolean);var  TopItem, I, FoundPos: Integer;begin  FoundPos := -1;  if SkipFirstItem then    TopItem := 1  else    TopItem := 0;  with List do  begin    for I := Count - 1 downto TopItem do      if JclBelongsHookedCode(Items[I].CallerAddr) then      begin        FoundPos := I;        Break;      end;    if FoundPos <> -1 then      for I := FoundPos downto TopItem do        Delete(I);  end;end;{$STACKFRAMES ON}procedure DoExceptionStackTrace(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean;  BaseOfStack: Pointer);var  IgnoreLevels: Integer;  FirstCaller: Pointer;  RawMode: Boolean;  Delayed: Boolean;begin  RawMode := stRawMode in JclStackTrackingOptions;  Delayed := stDelayedTrace in JclStackTrackingOptions;  if BaseOfStack = nil then  begin    BaseOfStack := GetFramePointer;    IgnoreLevels := 1;  end  else    IgnoreLevels := -1; // because of the "IgnoreLevels + 1" in TJclStackInfoList.StoreToList()  if OSException then  begin    if IgnoreLevels = -1 then      IgnoreLevels := 0    else      Inc(IgnoreLevels); // => HandleAnyException    FirstCaller := ExceptAddr;  end  else    FirstCaller := nil;  JclCreateStackList(RawMode, IgnoreLevels, FirstCaller, Delayed, BaseOfStack).CorrectOnAccess(OSException);end;function JclLastExceptStackList: TJclStackInfoList;begin  Result := GlobalStackList.ExceptStackInfo[GetCurrentThreadID];end;function JclLastExceptStackListToStrings(Strings: TStrings; IncludeModuleName, IncludeAddressOffset,  IncludeStartProcLineOffset, IncludeVAddress: Boolean): Boolean;var  List: TJclStackInfoList;begin  List := JclLastExceptStackList;  Result := Assigned(List);  if Result then    List.AddToStrings(Strings, IncludeModuleName, IncludeAddressOffset, IncludeStartProcLineOffset,      IncludeVAddress);end;function JclGetExceptStackList(ThreadID: DWORD): TJclStackInfoList;begin  Result := GlobalStackList.ExceptStackInfo[ThreadID];end;function JclGetExceptStackListToStrings(ThreadID: DWORD; Strings: TStrings;  IncludeModuleName: Boolean = False; IncludeAddressOffset: Boolean = False;  IncludeStartProcLineOffset: Boolean = False; IncludeVAddress: Boolean = False): Boolean;var  List: TJclStackInfoList;begin  List := JclGetExceptStackList(ThreadID);  Result := Assigned(List);  if Result then    List.AddToStrings(Strings, IncludeModuleName, IncludeAddressOffset, IncludeStartProcLineOffset,      IncludeVAddress);end;procedure JclClearGlobalStackData;begin  GlobalStackList.Clear;end;function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer): TJclStackInfoList;begin  Result := TJclStackInfoList.Create(Raw, AIgnoreLevels, FirstCaller, False, nil, nil);  GlobalStackList.AddObject(Result);end;function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;  DelayedTrace: Boolean): TJclStackInfoList;begin  Result := TJclStackInfoList.Create(Raw, AIgnoreLevels, FirstCaller, DelayedTrace, nil, nil);  GlobalStackList.AddObject(Result);end;function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;  DelayedTrace: Boolean; BaseOfStack: Pointer): TJclStackInfoList;begin  Result := TJclStackInfoList.Create(Raw, AIgnoreLevels, FirstCaller, DelayedTrace, BaseOfStack, nil);  GlobalStackList.AddObject(Result);end;function JclCreateStackList(Raw: Boolean; AIgnoreLevels: Integer; FirstCaller: Pointer;  DelayedTrace: Boolean; BaseOfStack, TopOfStack: Pointer): TJclStackInfoList;begin  Result := TJclStackInfoList.Create(Raw, AIgnoreLevels, FirstCaller, DelayedTrace, BaseOfStack, TopOfStack);  GlobalStackList.AddObject(Result);end;function GetThreadTopOfStack(ThreadHandle: THandle): TJclAddr;var  TBI: THREAD_BASIC_INFORMATION;  ReturnedLength: ULONG;begin  {$IFNDEF COMPILER37_UP}  Result := 0;  {$ENDIF ~COMPILER37_UP}  ReturnedLength := 0;  if (NtQueryInformationThread(ThreadHandle, ThreadBasicInformation, @TBI, SizeOf(TBI), @ReturnedLength) < $80000000) and     (ReturnedLength = SizeOf(TBI)) then    {$IFDEF CPU32}    Result := TJclAddr(PNT_TIB32(TBI.TebBaseAddress)^.StackBase)    {$ENDIF CPU32}    {$IFDEF CPU64}    Result := TJclAddr(PNT_TIB64(TBI.TebBaseAddress)^.StackBase)    {$ENDIF CPU64}  else    RaiseLastOSError;end;function JclCreateThreadStackTrace(Raw: Boolean; const ThreadHandle: THandle): TJclStackInfoList;var  ContextMemory: Pointer;  AlignedContext: PContext;begin  Result := nil;  ContextMemory := AllocMem(SizeOf(TContext) + 15);  try    if (TJclAddr(ContextMemory) and 15) <> 0 then      // PAnsiChar: TJclAddr is signed and would cause an int overflow for half the address space      AlignedContext := PContext(TJclAddr(PAnsiChar(ContextMemory) + 16) and -16)    else      AlignedContext := ContextMemory;    AlignedContext^.ContextFlags := CONTEXT_FULL;    {$IFDEF CPU32}    if GetThreadContext(ThreadHandle, AlignedContext^) then    begin      Result := JclCreateStackList(Raw, -1, Pointer(AlignedContext^.Eip), False, Pointer(AlignedContext^.Ebp),                  Pointer(GetThreadTopOfStack(ThreadHandle)));    end;    {$ENDIF CPU32}    {$IFDEF CPU64}    if GetThreadContext(ThreadHandle, AlignedContext^) then      Result := JclCreateStackList(Raw, -1, Pointer(AlignedContext^.Rip), False, Pointer(AlignedContext^.Rbp),                  Pointer(GetThreadTopOfStack(ThreadHandle)));    {$ENDIF CPU64}  finally    FreeMem(ContextMemory);  end;end;function JclCreateThreadStackTraceFromID(Raw: Boolean; ThreadID: DWORD): TJclStackInfoList;type  TOpenThreadFunc = function(DesiredAccess: DWORD; InheritHandle: BOOL; ThreadID: DWORD): THandle; stdcall;const  THREAD_GET_CONTEXT       = $0008;  THREAD_QUERY_INFORMATION = $0040;var  Kernel32Lib, ThreadHandle: THandle;  OpenThreadFunc: TOpenThreadFunc;begin  Result := nil;  Kernel32Lib := GetModuleHandle(kernel32);  if Kernel32Lib <> 0 then  begin    // OpenThread only exists since Windows ME    OpenThreadFunc := GetProcAddress(Kernel32Lib, 'OpenThread');    if Assigned(OpenThreadFunc) then    begin      ThreadHandle := OpenThreadFunc(THREAD_GET_CONTEXT or THREAD_QUERY_INFORMATION, False, ThreadID);      if ThreadHandle <> 0 then      try        Result := JclCreateThreadStackTrace(Raw, ThreadHandle);      finally        CloseHandle(ThreadHandle);      end;    end;  end;end;//=== { TJclStackInfoItem } ==================================================function TJclStackInfoItem.GetCallerAddr: Pointer;begin  Result := Pointer(FStackInfo.CallerAddr);end;function TJclStackInfoItem.GetLogicalAddress: TJclAddr;begin  Result := FStackInfo.CallerAddr - TJclAddr(ModuleFromAddr(CallerAddr));end;//=== { TJclStackInfoList } ==================================================constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;  AFirstCaller: Pointer);begin  Create(ARaw, AIgnoreLevels, AFirstCaller, False, nil, nil);end;constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;  AFirstCaller: Pointer; ADelayedTrace: Boolean);begin  Create(ARaw, AIgnoreLevels, AFirstCaller, ADelayedTrace, nil, nil);end;constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;  AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack: Pointer);begin  Create(ARaw, AIgnoreLevels, AFirstCaller, ADelayedTrace, ABaseOfStack, nil);end;constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;  AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack, ATopOfStack: Pointer);var  Item: TJclStackInfoItem;begin  inherited Create;  InterlockedIncrement(GlobalStackListLiveCount);  FIgnoreLevels := AIgnoreLevels;  FDelayedTrace := ADelayedTrace;  FRaw := ARaw;  BaseOfStack := TJclAddr(ABaseOfStack);  FStackOffset := 0;  FFramePointer := ABaseOfStack;  if ATopOfStack = nil then    TopOfStack := GetStackTop  else    TopOfStack := TJclAddr(ATopOfStack);  FModuleInfoList := GlobalModulesList.CreateModulesList;  if AFirstCaller <> nil then  begin    Item := TJclStackInfoItem.Create;    Item.FStackInfo.CallerAddr := TJclAddr(AFirstCaller);    Add(Item);  end;  {$IFDEF CPU32}  if DelayedTrace then    DelayStoreStack  else  if Raw then    TraceStackRaw  else    TraceStackFrames;  {$ENDIF CPU32}  {$IFDEF CPU64}  CaptureBackTrace;  {$ENDIF CPU64}end;destructor TJclStackInfoList.Destroy;begin  if Assigned(FStackData) then    FreeMem(FStackData);  GlobalModulesList.FreeModulesList(FModuleInfoList);  inherited Destroy;  if (InterlockedDecrement(GlobalStackListLiveCount) = 0) and JclDebugFinalized then    FreeJclDebugGlobals;end;{$IFDEF CPU64}procedure TJclStackInfoList.CaptureBackTrace;const  InternalSkipFrames = 1; // skip this methodvar  BackTrace: array [0..127] of Pointer;  MaxFrames: Integer;  Hash: DWORD;  I: Integer;  StackInfo: TStackInfo;  CapturedFramesCount: Word;begin  if JclCheckWinVersion(6, 0) then    MaxFrames := Length(BackTrace)  else  begin    // For XP and 2003 sum of FramesToSkip and FramesToCapture must be lower than 63    MaxFrames := 62 - InternalSkipFrames;  end;  ResetMemory(BackTrace, SizeOf(BackTrace));  CapturedFramesCount := CaptureStackBackTrace(InternalSkipFrames, MaxFrames, @BackTrace, Hash);  ResetMemory(StackInfo, SizeOf(StackInfo));  for I := 0 to CapturedFramesCount - 1 do  begin    StackInfo.CallerAddr := TJclAddr(BackTrace[I]);    StackInfo.Level := I;    StoreToList(StackInfo); // skips all frames with a level less than "IgnoreLevels"  end;end;{$ENDIF CPU64}procedure TJclStackInfoList.ForceStackTracing;begin  if DelayedTrace and Assigned(FStackData) and not FInStackTracing then  begin    FInStackTracing := True;    try      if Raw then        TraceStackRaw      else        TraceStackFrames;      if FCorrectOnAccess then        CorrectExceptStackListTop(Self, FSkipFirstItem);    finally      FInStackTracing := False;      FDelayedTrace := False;    end;  end;end;function TJclStackInfoList.GetCount: Integer;begin  ForceStackTracing;  Result := inherited Count;end;procedure TJclStackInfoList.CorrectOnAccess(ASkipFirstItem: Boolean);begin  FCorrectOnAccess := True;  FSkipFirstItem := ASkipFirstItem;end;procedure TJclStackInfoList.AddToStrings(Strings: TStrings; IncludeModuleName, IncludeAddressOffset,  IncludeStartProcLineOffset, IncludeVAddress: Boolean);var  I: Integer;  S: string;begin  ForceStackTracing;  Strings.BeginUpdate;  try    BeginGetLocationInfoCache;    try      for I := 0 to Count - 1 do      begin        S := GetLocationInfoStr(Items[I].CallerAddr, IncludeModuleName, IncludeAddressOffset,          IncludeStartProcLineOffset, IncludeVAddress);        Strings.Add(S);      end;    finally      EndGetLocationInfoCache;    end;  finally    Strings.EndUpdate;  end;end;function TJclStackInfoList.GetItems(Index: TJclListSize): TJclStackInfoItem;begin  ForceStackTracing;  Result := TJclStackInfoItem(Get(Index));end;function TJclStackInfoList.NextStackFrame(var StackFrame: PStackFrame; var StackInfo: TStackInfo): Boolean;var  CallInstructionSize: Cardinal;  StackFrameCallerFrame, NewFrame: TJclAddr;  StackFrameCallerAddr: TJclAddr;begin  // Only report this stack frame into the StockInfo structure  // if the StackFrame pointer, the frame pointer and the return address on the stack  // are valid addresses  StackFrameCallerFrame := StackInfo.CallerFrame;  while ValidStackAddr(TJclAddr(StackFrame)) do  begin    // CallersEBP above the previous CallersEBP    NewFrame := StackFrame^.CallerFrame;    if NewFrame <= StackFrameCallerFrame then      Break;    StackFrameCallerFrame := NewFrame;    // CallerAddr within current process space, code segment etc.    // CallerFrame within current thread stack. Added Mar 12 2002 per Hallvard's suggestion    StackFrameCallerAddr := StackFrame^.CallerAddr;    if ValidCodeAddr(StackFrameCallerAddr, FModuleInfoList) and ValidStackAddr(StackFrameCallerFrame + FStackOffset) then    begin      Inc(StackInfo.Level);      StackInfo.StackFrame := StackFrame;      StackInfo.ParamPtr := PDWORD_PTRArray(TJclAddr(StackFrame) + SizeOf(TStackFrame));      if StackFrameCallerFrame > StackInfo.CallerFrame then        StackInfo.CallerFrame := StackFrameCallerFrame      else        // the frame pointer points to an address that is below        // the last frame pointer, so it must be invalid        Break;      // Calculate the address of caller by subtracting the CALL instruction size (if possible)      if ValidCallSite(StackFrameCallerAddr, CallInstructionSize) then        StackInfo.CallerAddr := StackFrameCallerAddr - CallInstructionSize      else        StackInfo.CallerAddr := StackFrameCallerAddr;      // the stack may be messed up in big projects, avoid overflow in arithmetics      if StackFrameCallerFrame + FStackOffset < TJclAddr(StackFrame) then        Break;      StackInfo.DumpSize := StackFrameCallerFrame + FStackOffset - TJclAddr(StackFrame);      StackInfo.ParamSize := (StackInfo.DumpSize - SizeOf(TStackFrame)) div 4;      if PStackFrame(StackFrame^.CallerFrame + FStackOffset) = StackFrame then        Break;      // Step to the next stack frame by following the frame pointer      StackFrame := PStackFrame(StackFrameCallerFrame + FStackOffset);      Result := True;      Exit;    end;    // Step to the next stack frame by following the frame pointer    StackFrame := PStackFrame(StackFrameCallerFrame + FStackOffset);  end;  Result := False;end;procedure TJclStackInfoList.StoreToList(const StackInfo: TStackInfo);var  Item: TJclStackInfoItem;begin  if ((IgnoreLevels = -1) and (StackInfo.Level > 0)) or     (StackInfo.Level > (IgnoreLevels + 1)) then  begin    Item := TJclStackInfoItem.Create;    Item.FStackInfo := StackInfo;    Add(Item);  end;end;procedure TJclStackInfoList.TraceStackFrames;var  StackFrame: PStackFrame;  StackInfo: TStackInfo;begin  Capacity := 32; // reduce ReallocMem calls, must be > 1 because the caller's EIP register is already in the list  // Start at level 0  StackInfo.Level := 0;  StackInfo.CallerFrame := 0;  if DelayedTrace then    // Get the current stack frame from the frame register    StackFrame := FFramePointer  else  begin    // We define the bottom of the valid stack to be the current ESP pointer    if BaseOfStack = 0 then      BaseOfStack := TJclAddr(GetFramePointer);    // Get a pointer to the current bottom of the stack    StackFrame := PStackFrame(BaseOfStack);  end;  // We define the bottom of the valid stack to be the current frame Pointer  // There is a TIB field called pvStackUserBase, but this includes more of the  // stack than what would define valid stack frames.  BaseOfStack := TJclAddr(StackFrame) - 1;  // Loop over and report all valid stackframes  while NextStackFrame(StackFrame, StackInfo) and (inherited Count <> MaxStackTraceItems) do    StoreToList(StackInfo);end;function TraceStackInstuctions(Proc, InstructionAddr: Pointer; ModuleEndAddr: TJclAddr;  var LocalVarStackOffset, ParamStackOffset: Integer): Boolean;const  PointerSize = SizeOf(Pointer);  function ParseSaveRegisters(ProcAddr, CallAddr: TJclAddr; var RegisterStackOffset: Integer): TJclAddr;  var    P: PByteArray;  begin    Result := ProcAddr;    while Result < TJclAddr(CallAddr) do    begin      P := PByteArray(Result);      if (P[0] and $F8) = $50 then // PUSH r32      begin        Inc(RegisterStackOffset, PointerSize);        Inc(Result);        Continue;      end;      Break;    end;  end;  function CheckRegisterRestoreBackwards(ProcAddr, CallAddr: TJclAddr; var RegisterStackOffset: Integer): Boolean;  var    Count: Integer;  begin    if RegisterStackOffset > 0 then    begin      Count := 0;      while (ProcAddr > CallAddr) and (PByte(ProcAddr)^ and $F8 = $58) do // POP r32      begin        Dec(ProcAddr);        Inc(Count);      end;      if (Count > 0) and (Cardinal(Count) <= Cardinal(RegisterStackOffset) div PointerSize) then      begin        // We may have used a "function call push" in the prolog analysis so fix this        RegisterStackOffset := Count * PointerSize;        Result := True;      end      else        Result := False;    end    else      Result := True;  end;  function ParseEspChange(ProcAddr, CallAddr: TJclAddr; var LocalVarStackOffset: Integer; var EspChangeFound: Boolean): TJclAddr;  var    P: PByteArray;  begin    Result := ProcAddr;    P := PByteArray(Result);    if (Result + 3 < TJclAddr(CallAddr)) and (P[0] = $83) and (P[1] = $C4) then // 83C4F8           add esp,imm8    begin      Inc(LocalVarStackOffset, -Integer(ShortInt(P[2])));      EspChangeFound := True;      Inc(Result, 3);    end    else if (Result + 6 < TJclAddr(CallAddr)) and (P[0] = $81) and (P[1] = $C4) then // 81C408000100     add esp,imm32    begin      Inc(LocalVarStackOffset, -PInteger(@P[2])^);      EspChangeFound := True;      Inc(Result, 6);    end;  end;  function CheckEspChangeBackwards(ProcAddr, CallAddr: TJclAddr): Boolean;  var    Offset: Integer;  begin    Inc(ProcAddr);    Result := False;    if ProcAddr - 3 >= CallAddr then    begin      ParseEspChange(ProcAddr - 3, ProcAddr + 1, Offset, Result);      if Result then        Exit;    end;    if ProcAddr - 6 >= CallAddr then    begin      ParseEspChange(ProcAddr - 6, ProcAddr + 1, Offset, Result);      if Result then        Exit;    end;  end;  function CheckStackAddressValidation(ProcAddr, CallAddr: TJclAddr; var LocalVarStackOffset: Integer;    var EspChangeFound: Boolean): Integer;  var    P: PByteArray;  begin    // The compiler emits multiple functino prologues to probe the stack frame memory pages.    P := PByteArray(ProcAddr);    if (ProcAddr + 6 < CallAddr) and       (P[0] = $81) and (P[1] = $C4) and (PInteger(@P[2])^ = -4092) and     // 81C404F0FFFF     add esp,$fffff004       (P[6] = $50) then                                                    // 50               push eax    begin      Inc(LocalVarStackOffset, (4092+4));      EspChangeFound := True;      Result := 7;    end    else if (ProcAddr + 8 < CallAddr) and                                   // CompilerSpeedPack option -x-fpr       (P[0] = $81) and (P[1] = $C4) and (PInteger(@P[2])^ = -4096) and     // 81C404F0FFFF     add esp,$fffff000       (P[6] = $85) and (P[7] = $24) and (P[8] = $24) then                  // 852424           test [esp],esp    begin      Inc(LocalVarStackOffset, 4096);      EspChangeFound := True;      Result := 9;    end    else if (ProcAddr + 17 + 4 < CallAddr) and       (P[0] = $50) and                                                     // 50               push eax       (P[1] = $B8) and                                                     // B804000000       mov eax,imm32       (P[6] = $81) and (P[7] = $C4) and (PInteger(@P[8])^ = -4092) and     // 81C404F0FFFF     add esp,$fffff004       (P[12] = $50) and                                                    // 50               push eax       (P[13] = $48) and                                                    // 48               dec eax       (P[14] = $75) and (P[15] = $F6) and                                  // 75F6             jnz -10       (P[16] = $8B) and ((PWord(@P[16])^ = $2484) or (P[17] = $45)) then   // 8B842400000100   mov eax,[esp+imm32] / 8B45FC mov eax,[ebp-imm8]   begin      Inc(LocalVarStackOffset, PInteger(@P[2])^ * (4092+4));      EspChangeFound := True;      Result := 19;      if P[17] = $45 then        Inc(Result, 1)  // 8B45FC           mov eax,[ebp-imm8]      else        Inc(Result, 4); // 8B842400000100   mov eax,[esp+imm32]    end    else if (ProcAddr + 20 + 4 < CallAddr) and                              // CompilerSpeedPack option -x-fpr       (P[0] = $50) and                                                     // 50               push eax       (P[1] = $B8) and                                                     // B804000000       mov eax,imm32       (P[6] = $81) and (P[7] = $C4) and (PInteger(@P[8])^ = -4096) and     // 81C404F0FFFF     add esp,$fffff000       (P[12] = $85) and (P[13] = $24) and (P[14] = $24) and                // 852424           test [esp],esp       (P[15] = $48) and                                                    // 48               dec eax       (P[16] = $75) and (P[17] = $F6) and                                  // 75F6             jnz -10       (P[18] = $8B) and ((PWord(@P[19])^ = $2484) or (P[19] = $45)) then   // 8B842400000100   mov eax,[esp+imm32] / 8B45FC mov eax,[ebp-imm8]    begin      Inc(LocalVarStackOffset, PInteger(@P[2])^ * 4096);      EspChangeFound := True;      Result := 21;      if P[19] = $45 then        Inc(Result, 1)  // 8B45FC           mov eax,[ebp-imm8]      else        Inc(Result, 4); // 8B842400000100   mov eax,[esp+imm32]    end    else if (ProcAddr + 2 < CallAddr) and       (P[0] = $33) and (P[1] = $C9) and                                   // 33C9             xor ecx,ecx       (P[2] = $51) then                                                   // 51               push ecx    begin      Inc(LocalVarStackOffset, 4);      EspChangeFound := True;      Result := 1;      Inc(ProcAddr, 3);      while (ProcAddr + 2 < CallAddr) and (PByte(ProcAddr)^ = $51) do      begin        Inc(ProcAddr);        Inc(Result);      end;      Inc(LocalVarStackOffset, 4 * Result);      Inc(Result, 2); // xor ecx, ecx    end    // Compiler sets the stack for managed local variables to zero    else if (ProcAddr + 12 < CallAddr) and       (P[0] = $51) and                                                     // 51               push ecx       (P[1] = $B9) and // imm32                                            // B906000000       mov ecx,imm32       (P[6] = $6A) and (P[7] = $00) and                                    // 6A00             push $00       (P[8] = $6A) and (P[9] = $00) and                                    // 6A00             push $00       (P[10] = $49) and                                                    // 49               dec ecx       (P[11] = $75) and (P[12] = $F9) then                                 // 75F9             jnz -7    begin      Inc(LocalVarStackOffset, PInteger(@P[2])^ * PointerSize * 2);      EspChangeFound := True;      Result := 13;      // For an odd number of local DWORDs the compiler emits an additional "push ecx"      if (ProcAddr + 13 < CallAddr) and         (P[13] = $51) then                                                 // 51               push ecx      begin        Inc(LocalVarStackOffset, PointerSize);        Inc(Result, 1);      end;      if (ProcAddr + TJclAddr(Result) + 3 < CallAddr) and         (P[Result + 0] = $87) and (P[Result + 1] = $4D) then // imm8      // 874DFC           xchg [ebp-imm8],ecx      begin        Inc(Result, 3);      end      else if (ProcAddr + TJclAddr(Result) + 10 < CallAddr) and                        // CompilerSpeedPack option -x-fpr         (P[Result + 0] = $8B) and (P[Result + 1] = $4D) and //imm8                    // 8B4DFC           mov ecx,[ebp-imm8]         (P[Result + 3] = $C7) and (P[Result + 4] = $45) and (P[Result + 5] = $FC) and // C745FC00000000   mov [ebp-$04],$00000000         (PInteger(@P[Result + 6])^ = 0) then      begin        Inc(Result, 10);      end;    end    else      Result := 0;  end;var  P: PByteArray;  ProcAddr, CallAddr, EpilogAddr: TJclAddr;  StackFrameFound: Integer;  RegisterStackOffset: Integer;  EspChangeFound: Boolean;  Size: Integer;  PossibleEndFound: Boolean;  EpilogInfo: TJclLocationInfo;  RegStackOffset: Integer;begin  LocalVarStackOffset := 0;  ParamStackOffset := 0;  RegisterStackOffset := 0;  Result := False;  if Proc = nil then    Exit;  ProcAddr := TJclAddr(Proc);  CallAddr := TJclAddr(InstructionAddr);  // Prolog: stackframe  StackFrameFound := 0;  EspChangeFound := False;  if ProcAddr < CallAddr then  begin    P := PByteArray(ProcAddr);    if (P[0] = $55) and                                           // PUSH EBP       (P[1] = $8B) and (P[2] = $EC) then                         // MOV EBP,ESP    begin      LocalVarStackOffset := PointerSize; // EBP      StackFrameFound := 1; // Epilog must end with "POP EBP"      Inc(ProcAddr, 3);    end    else if (P[0] = $C8) and (ProcAddr + 4 < CallAddr) then  // ENTER Size(Word), NestingLevel(Byte)    begin      LocalVarStackOffset := PointerSize + PWord(@P[1])^ + PointerSize*P[3]; // EBP + Size + 4*NestingLevel      StackFrameFound := -1; // Epilog must end with "LEAVE"      Inc(ProcAddr, 4);    end;  end;  if StackFrameFound = 0 then  begin    // Prolog: save registers    // If we have no stackframe, then the compiler saves the registers before allocating stack variables.    // RegisterStackOffset is preliminary because it may be reset by Epilog's POP code that is more    // accurate because we can't distinguish between the save register and an immediatelly following    // function parameter "PUSH".    ProcAddr := ParseSaveRegisters(ProcAddr, CallAddr, {var} RegisterStackOffset);    // Prolog: no stackframe + stack address validation    Size := 0;    if RegisterStackOffset >= PointerSize then    begin      // If there is a "push eax", then the ParseSaveRegisters handled it, but it may be the      // stack validation's "push eax".      Size := CheckStackAddressValidation(ProcAddr - 1{push eax}, CallAddr, {var} LocalVarStackOffset, {var} EspChangeFound);      if Size > 0 then      begin        Dec(RegisterStackOffset, PointerSize);        Dec(ProcAddr);      end;    end;    if Size = 0 then      Size := CheckStackAddressValidation(ProcAddr, CallAddr, {var} LocalVarStackOffset, {var} EspChangeFound);    Inc(ProcAddr, Size);    ProcAddr := ParseEspChange(ProcAddr, CallAddr, LocalVarStackOffset, {var} EspChangeFound);  end  else  begin    // Prolog: stackframe + stack address validation    Size := CheckStackAddressValidation(ProcAddr, CallAddr, {var} LocalVarStackOffset, {var} EspChangeFound);    Inc(ProcAddr, Size);    ProcAddr := ParseEspChange(ProcAddr, CallAddr, LocalVarStackOffset, {var} EspChangeFound);    // If we have a stackframe, then the compiler saves the registers after allocating stack variables.    ProcAddr := ParseSaveRegisters(ProcAddr, CallAddr, {var} RegisterStackOffset);  end;  // Find not closed try/finally/except blocks and add them the LocalVarStackOffset  while (ProcAddr < CallAddr) and (ProcAddr < ModuleEndAddr) do  begin    // fast forward find for XOR EAX,EAX    while (ProcAddr < CallAddr) and (ProcAddr < ModuleEndAddr) and (PByteArray(ProcAddr)[0] <> $33) do      Inc(ProcAddr);    P := PByteArray(ProcAddr);    // Find all occurrences above the CallAddr and add to LocalVarStackOffset (3*PointerSize)    // "try"    // 33C0             xor eax,eax    // 55               push ebp    // 68E9E05000       push $0050e0e9    // 64FF30           push dword ptr fs:[eax]    // 648920           mov fs:[eax],esp    if (ProcAddr + 13 < CallAddr) and       (P[0] = $33) and (P[1] = $C0) and       (P[2] = $55) and       (P[3] = $68) and       (P[8] = $64) and (P[9] = $FF) and (P[10] = $30) and       (P[11] = $64) and (P[12] = $89) and (P[13] = $20) then    begin      Inc(LocalVarStackOffset, 3 * PointerSize);    end    // "finally"/"except"    // Find all occurrences above the CallAddr and substract from LocalVarStackOffset (3*PointerSize)    // 33C0             xor eax,eax    // 5A               pop edx    // 59               pop ecx    // 59               pop ecx    // 648910           mov fs:[eax],edx    else if (ProcAddr + 7 < CallAddr) and       (P[0] = $33) and (P[1] = $C0) and       (P[2] = $5A) and       (P[3] = $59) and       (P[4] = $59) and       (P[5] = $64) and (P[6] = $89) and (P[7] = $10) then    begin      Dec(LocalVarStackOffset, 3 * PointerSize);    end;    Inc(ProcAddr);  end;  // Find the epilog to obtain the ParamStackOffset (would be much easier and less guess work  // if we knew the exact function's end address)  ProcAddr := CallAddr;  while ProcAddr < ModuleEndAddr do  begin    // fast forward find for RET / RET imm16    while (ProcAddr < ModuleEndAddr) and not (PByteArray(ProcAddr)[0] in [$C3, $C2]) do      Inc(ProcAddr);    P := PByteArray(ProcAddr);    // We may have found the RET of a finally clause    if (ProcAddr + 7 < ModuleEndAddr) and // skip "finally" code       (P[0] = $C3) and                            // C3               ret       (P[1] = $E9) and                            // E91821FAFF       jmp @HandleFinally       (P[6] = $EB) and (ShortInt(P[7]) < 0) then  // EBF8             jmp imm8    begin      Inc(ProcAddr, 8);    end    else if (ProcAddr + 10 < ModuleEndAddr) and // skip "finally" code       (P[0] = $C3) and                               // C3               ret       (P[1] = $E9) and                               // E91821FAFF       jmp @HandleFinally       (P[6] = $E9) and (PInteger(@P[7])^ < 0) then   // E9xxxxxxxx       jmp imm32    begin      Inc(ProcAddr, 11);    end    else if (P[0] = $C3) or ((P[0] = $C2) and (ProcAddr + 3 < ModuleEndAddr)) then    begin      EpilogAddr := ProcAddr;      PossibleEndFound := False;      if StackFrameFound = 1 then      begin        // If we have a stackframe, then we verify that the stackframe is cleared to check        // if we found a valid "RET"        if EspChangeFound then          EpilogAddr := EpilogAddr - 3        else          EpilogAddr := EpilogAddr - 1;        if EpilogAddr >= CallAddr then        begin          P := PByteArray(EpilogAddr);          if EspChangeFound and             (P[0] = $8B) and (P[1] = $E5) and  // 8BE5             mov esp,ebp             (P[2] = $5D) then                  // 5D               pop ebp          begin            Dec(EpilogAddr);            PossibleEndFound := True;          end          else if not EspChangeFound and             (P[0] = $5D) then                  // 5D               pop ebp          begin            Dec(EpilogAddr);            PossibleEndFound := True;          end;        end;      end      else if StackFrameFound = -1 then      begin        // If we have a ENTER/LEAVE stackframe, then we verify that the stackframe is cleared        // to check if we found a valid "RET"        Dec(EpilogAddr);        P := PByteArray(EpilogAddr);        if (EpilogAddr >= CallAddr) and (P[0] = $C9) then  // LEAVE        begin          Dec(EpilogAddr);          PossibleEndFound := True;        end;      end      else      begin        // If we have no stackframe, then we can't verify the validity of the "RET" here        EpilogAddr := EpilogAddr - 1;        PossibleEndFound := True;      end;      if PossibleEndFound then      begin        if GetLocationInfo(Pointer(EpilogAddr), EpilogInfo) and           (TJclAddr(EpilogInfo.OffsetFromProcName) <> EpilogAddr - TJclAddr(Proc)) then        begin          // If we didn't find a RET in the same procedure then the analysis failed          Exit;        end;        if PossibleEndFound then        begin          // If we have registers saved on the stack, we can use those to verify if the          // found "RET" is valid.          RegStackOffset := RegisterStackOffset;          if CheckRegisterRestoreBackwards(EpilogAddr, CallAddr, {var} RegStackOffset) then          begin            if (StackFrameFound = 0) and EspChangeFound then            begin              // If we have local variables (ESP was changed in the prolog) we can use that              // information to verify the "RET"              EpilogAddr := EpilogAddr - TJclAddr(RegStackOffset) div PointerSize;              if not CheckEspChangeBackwards(EpilogAddr, CallAddr) then                PossibleEndFound := False;            end;            if PossibleEndFound then            begin              RegisterStackOffset := RegStackOffset;              if PByte(ProcAddr)^ = $C2 then                ParamStackOffset := PWord(ProcAddr + 1)^              else              begin                // TODO: if we only have a "RET" at the end we need to look at the call instruction                // if it is followed by a "sub/add esp,xx" for a "cdecl" function. (What if the add/sub                // is for the caller's epilog?)              end;              Break;            end;          end;        end;      end;    end;    Inc(ProcAddr);  end;  Inc(LocalVarStackOffset, RegisterStackOffset);  Result := True;end;procedure TJclStackInfoList.TraceStackRaw;var  StackInfo: TStackInfo;  StackPtr: PJclAddr;  PrevCaller: TJclAddr;  CallInstructionSize: Cardinal;  StackTop: TJclAddr;  ProcInfo: TJclLocationInfo;  ProcStart: Pointer;  CallInstructionPtr: Pointer;  LocalVarStackOffset, ParamStackOffset: Integer;  ModuleEndAddr: TJclAddr;begin  Capacity := 32; // reduce ReallocMem calls, must be > 1 because the caller's EIP register is already in the list  if DelayedTrace then  begin    if not Assigned(FStackData) then      Exit;    StackPtr := PJclAddr(FStackData);  end  else  begin    // We define the bottom of the valid stack to be the current ESP pointer    if BaseOfStack = 0 then      BaseOfStack := TJclAddr(GetStackPointer);    // Get a pointer to the current bottom of the stack    StackPtr := PJclAddr(BaseOfStack);  end;  StackTop := TopOfStack;  // We will not be able to fill in all the fields in the StackInfo record,  // so just blank it all out first  ResetMemory(StackInfo, SizeOf(StackInfo));  // Clear the previous call address  PrevCaller := 0;  // stCleanRawStack: We don't know the number of parameters for the "initial" function  ParamStackOffset := 0;  if stCleanRawStack in JclStackTrackingOptions then    BeginGetLocationInfoCache; // speed up the GetLocationInfo calls  // Loop through all of the valid stack space  try    while (TJclAddr(StackPtr) < StackTop) and (inherited Count <> MaxStackTraceItems) do    begin      // If the current DWORD on the stack refers to a valid call site...      if ValidCallSite(StackPtr^, CallInstructionSize) and (StackPtr^ <> PrevCaller) then      begin        // then pick up the callers address        StackInfo.CallerAddr := StackPtr^ - CallInstructionSize;        // remember to callers address so that we don't report it repeatedly        PrevCaller := StackPtr^;        // increase the stack level        Inc(StackInfo.Level);        // then report it back to our caller        StoreToList(StackInfo);        if stCleanRawStack in JclStackTrackingOptions then        begin          // Skip all stack parameters of the last called function          Inc(PByte(StackPtr), ParamStackOffset);          ParamStackOffset := 0;          CallInstructionPtr := Pointer(StackInfo.CallerAddr);          if GetLocationInfo(CallInstructionPtr, ProcInfo) then          begin            if ProcInfo.ProcedureName <> '' then            begin              if (ProcInfo.ProcedureName[1] = '@') and (ProcInfo.ProcedureName = '@RaiseExcept$qqrv') then              begin                // Special handling for _RaiseExcept because it does a lot to the stack including                // putting the ExceptAddr multiple times on the stack causing TraceStackInstuctions to                // change the StackPtr to the wrong locations.                LocalVarStackOffset := 17 * SizeOf(Pointer);                ParamStackOffset := 6 * SizeOf(Pointer);                Inc(PByte(StackPtr), LocalVarStackOffset);              end              else              begin                ProcStart := Pointer(TJclAddr(CallInstructionPtr) - TJclAddr(ProcInfo.OffsetFromProcName));                ModuleEndAddr := TJclAddr(ProcInfo.DebugInfo.Module) + ModuleCodeOffset + TJclAddr(ProcInfo.DebugInfo.ModuleCodeSize);                if TraceStackInstuctions(ProcStart, CallInstructionPtr, ModuleEndAddr, LocalVarStackOffset, ParamStackOffset) then                  Inc(PByte(StackPtr), LocalVarStackOffset) // skip all local variables (and saved registers)                else                  ParamStackOffset := 0; // Don't skip stack entries if TraceStackInstuctions failed              end;            end;          end;        end;      end;      // Look at the next DWORD on the stack      Inc(StackPtr);    end;  finally    if stCleanRawStack in JclStackTrackingOptions then      EndGetLocationInfoCache;    if Assigned(FStackData) then    begin      FreeMem(FStackData);      FStackData := nil;    end;  end;end;{$IFDEF CPU32}procedure TJclStackInfoList.DelayStoreStack;var  StackPtr: PJclAddr;  StackDataSize: Cardinal;begin  if Assigned(FStackData) then  begin    FreeMem(FStackData);    FStackData := nil;  end;  // We define the bottom of the valid stack to be the current ESP pointer  if BaseOfStack = 0 then  begin    BaseOfStack := TJclAddr(GetStackPointer);    FFramePointer := GetFramePointer;  end;  // Get a pointer to the current bottom of the stack  StackPtr := PJclAddr(BaseOfStack);  if TJclAddr(StackPtr) < TopOfStack then  begin    StackDataSize := TopOfStack - TJclAddr(StackPtr);    GetMem(FStackData, StackDataSize);    System.Move(StackPtr^, FStackData^, StackDataSize);  end;  FStackOffset := Int64(FStackData) - Int64(StackPtr);  FFramePointer := Pointer(TJclAddr(FFramePointer) + FStackOffset);  TopOfStack := TopOfStack + FStackOffset;end;{$ENDIF CPU32}// Validate that the code address is a valid code site//// Information from Intel Manual 24319102(2).pdf, Download the 6.5 MBs from:// http://developer.intel.com/design/pentiumii/manuals/243191.htm// Instruction format, Chapter 2 and The CALL instruction: page 3-53, 3-54function TJclStackInfoList.ValidCallSite(CodeAddr: TJclAddr; out CallInstructionSize: Cardinal): Boolean;var  CodeDWORD4: DWORD;  CodeDWORD8: DWORD;  C4P, C8P: PDWORD;  RM1, RM2, RM5: Byte;begin  // todo: 64 bit version  // First check that the address is within range of our code segment!  Result := CodeAddr > 8;  if Result then  begin    C8P := PDWORD(CodeAddr - 8);    C4P := PDWORD(CodeAddr - 4);    Result := ValidCodeAddr(TJclAddr(C8P), FModuleInfoList) and not IsBadReadPtr(C8P, 8);    // Now check to see if the instruction preceding the return address    // could be a valid CALL instruction    if Result then    begin      try        CodeDWORD8 := PDWORD(C8P)^;        CodeDWORD4 := PDWORD(C4P)^;        // CodeDWORD8 = (ReturnAddr-5):(ReturnAddr-6):(ReturnAddr-7):(ReturnAddr-8)        // CodeDWORD4 = (ReturnAddr-1):(ReturnAddr-2):(ReturnAddr-3):(ReturnAddr-4)        // ModR/M bytes contain the following bits:        // Mod        = (76)        // Reg/Opcode = (543)        // R/M        = (210)        RM1 := (CodeDWORD4 shr 24) and $7;        RM2 := (CodeDWORD4 shr 16) and $7;        //RM3 := (CodeDWORD4 shr 8)  and $7;        //RM4 :=  CodeDWORD4         and $7;        RM5 := (CodeDWORD8 shr 24) and $7;        //RM6 := (CodeDWORD8 shr 16) and $7;        //RM7 := (CodeDWORD8 shr 8)  and $7;        // Check the instruction prior to the potential call site.        // We consider it a valid call site if we find a CALL instruction there        // Check the most common CALL variants first        if ((CodeDWORD8 and $FF000000) = $E8000000) then          // 5 bytes, "CALL NEAR REL32" (E8 cd)          CallInstructionSize := 5        else        if ((CodeDWORD4 and $F8FF0000) = $10FF0000) and not (RM1 in [4, 5]) then          // 2 bytes, "CALL NEAR [EAX]" (FF /2) where Reg = 010, Mod = 00, R/M <> 100 (1 extra byte)          // and R/M <> 101 (4 extra bytes)          CallInstructionSize := 2        else        if ((CodeDWORD4 and $F8FF0000) = $D0FF0000) then          // 2 bytes, "CALL NEAR EAX" (FF /2) where Reg = 010 and Mod = 11          CallInstructionSize := 2        else        if ((CodeDWORD4 and $00FFFF00) = $0014FF00) then          // 3 bytes, "CALL NEAR [EAX+EAX*i]" (FF /2) where Reg = 010, Mod = 00 and RM = 100          // SIB byte not validated          CallInstructionSize := 3        else        if ((CodeDWORD4 and $00F8FF00) = $0050FF00) and (RM2 <> 4) then          // 3 bytes, "CALL NEAR [EAX+$12]" (FF /2) where Reg = 010, Mod = 01 and RM <> 100 (1 extra byte)          CallInstructionSize := 3        else        if ((CodeDWORD4 and $0000FFFF) = $000054FF) then          // 4 bytes, "CALL NEAR [EAX+EAX+$12]" (FF /2) where Reg = 010, Mod = 01 and RM = 100          // SIB byte not validated          CallInstructionSize := 4        else        if ((CodeDWORD8 and $FFFF0000) = $15FF0000) then          // 6 bytes, "CALL NEAR [$12345678]" (FF /2) where Reg = 010, Mod = 00 and RM = 101          CallInstructionSize := 6        else        if ((CodeDWORD8 and $F8FF0000) = $90FF0000) and (RM5 <> 4) then          // 6 bytes, "CALL NEAR [EAX+$12345678]" (FF /2) where Reg = 010, Mod = 10 and RM <> 100 (1 extra byte)          CallInstructionSize := 6        else        if ((CodeDWORD8 and $00FFFF00) = $0094FF00) then          // 7 bytes, "CALL NEAR [EAX+EAX+$1234567]" (FF /2) where Reg = 010, Mod = 10 and RM = 100          CallInstructionSize := 7        else        if ((CodeDWORD8 and $0000FF00) = $00009A00) then          // 7 bytes, "CALL FAR $1234:12345678" (9A ptr16:32)          CallInstructionSize := 7        else          Result := False;        // Because we're not doing a complete disassembly, we will potentially report        // false positives. If there is odd code that uses the CALL 16:32 format, we        // can also get false negatives.      except        Result := False;      end;    end;  end;end;{$IFNDEF STACKFRAMES_ON}{$STACKFRAMES OFF}{$ENDIF ~STACKFRAMES_ON}function TJclStackInfoList.ValidStackAddr(StackAddr: TJclAddr): Boolean;begin  Result := (BaseOfStack < StackAddr) and (StackAddr < TopOfStack);end;//=== Exception frame info routines ==========================================function JclCreateExceptFrameList(AIgnoreLevels: Integer): TJclExceptFrameList;begin  Result := TJclExceptFrameList.Create(AIgnoreLevels);  GlobalStackList.AddObject(Result);end;function JclLastExceptFrameList: TJclExceptFrameList;begin  Result := GlobalStackList.LastExceptFrameList[GetCurrentThreadID];end;function JclGetExceptFrameList(ThreadID: DWORD): TJclExceptFrameList;begin  Result := GlobalStackList.LastExceptFrameList[ThreadID];end;procedure DoExceptFrameTrace;begin  // Ignore first 2 levels; the First level is an undefined frame (I haven't a  // clue as to where it comes from. The second level is the try..finally block  // in DoExceptNotify.  JclCreateExceptFrameList(4);end;{$OVERFLOWCHECKS OFF}function GetJmpDest(Jmp: PJmpInstruction): Pointer;begin  // TODO : 64 bit version  if Jmp^.opCode = $E9 then    Result := Pointer(TJclAddr(Jmp) + TJclAddr(Jmp^.distance) + 5)  else  if Jmp.opCode = $EB then    Result := Pointer(TJclAddr(Jmp) + TJclAddr(ShortInt(Jmp^.distance)) + 2)  else    Result := nil;  if (Result <> nil) and (PJmpTable(Result).OPCode = $25FF) then    if not IsBadReadPtr(PJmpTable(Result).Ptr, SizeOf(Pointer)) then      Result := Pointer(PJclAddr(PJmpTable(Result).Ptr)^);end;{$IFDEF OVERFLOWCHECKS_ON}{$OVERFLOWCHECKS ON}{$ENDIF OVERFLOWCHECKS_ON}//=== { TJclExceptFrame } ====================================================constructor TJclExceptFrame.Create(AFrameLocation: Pointer; AExcDesc: PExcDesc);begin  inherited Create;  FFrameKind := efkUnknown;  FFrameLocation := AFrameLocation;  FCodeLocation := nil;  AnalyseExceptFrame(AExcDesc);end;{$RANGECHECKS OFF}procedure TJclExceptFrame.AnalyseExceptFrame(AExcDesc: PExcDesc);var  Dest: Pointer;  LocInfo: TJclLocationInfo;  FixedProcedureName: string;  DotPos, I: Integer;begin  Dest := GetJmpDest(@AExcDesc^.Jmp);  if Dest <> nil then  begin    // get frame kind    LocInfo := GetLocationInfo(Dest);    if CompareText(LocInfo.UnitName, 'system') = 0 then    begin      FixedProcedureName := LocInfo.ProcedureName;      DotPos := Pos('.', FixedProcedureName);      if DotPos > 0 then        FixedProcedureName := Copy(FixedProcedureName, DotPos + 1, Length(FixedProcedureName) - DotPos);      if CompareText(FixedProcedureName, '@HandleAnyException') = 0 then        FFrameKind := efkAnyException      else      if CompareText(FixedProcedureName, '@HandleOnException') = 0 then        FFrameKind := efkOnException      else      if CompareText(FixedProcedureName, '@HandleAutoException') = 0 then        FFrameKind := efkAutoException      else      if CompareText(FixedProcedureName, '@HandleFinally') = 0 then        FFrameKind := efkFinally;    end;    // get location    if FFrameKind <> efkUnknown then    begin      FCodeLocation := GetJmpDest(PJmpInstruction(TJclAddr(@AExcDesc^.Instructions)));      if FCodeLocation = nil then        FCodeLocation := @AExcDesc^.Instructions;    end    else    begin      FCodeLocation := GetJmpDest(PJmpInstruction(TJclAddr(AExcDesc)));      if FCodeLocation = nil then        FCodeLocation := AExcDesc;    end;    // get on handlers    if FFrameKind = efkOnException then    begin      SetLength(FExcTab, AExcDesc^.Cnt);      for I := 0 to AExcDesc^.Cnt - 1 do      begin        if AExcDesc^.ExcTab[I].VTable = nil then        begin          SetLength(FExcTab, I);          Break;        end        else          FExcTab[I] := AExcDesc^.ExcTab[I];      end;    end;  end;end;{$IFDEF RANGECHECKS_ON}{$RANGECHECKS ON}{$ENDIF RANGECHECKS_ON}function TJclExceptFrame.Handles(ExceptObj: TObject): Boolean;var  Handler: Pointer;begin  Result := HandlerInfo(ExceptObj, Handler);end;{$OVERFLOWCHECKS OFF}function TJclExceptFrame.HandlerInfo(ExceptObj: TObject; out HandlerAt: Pointer): Boolean;var  I: Integer;  ObjVTable, VTable, ParentVTable: Pointer;begin  Result := FrameKind in [efkAnyException, efkAutoException];  if not Result and (FrameKind = efkOnException) then  begin    HandlerAt := nil;    ObjVTable := Pointer(ExceptObj.ClassType);    for I := Low(FExcTab) to High(FExcTab) do    begin      VTable := ObjVTable;      Result := FExcTab[I].VTable = nil;      while (not Result) and (VTable <> nil) do      begin        Result := (FExcTab[I].VTable = VTable) or          (PShortString(PPointer(PJclAddr(FExcTab[I].VTable)^ + TJclAddr(vmtClassName))^)^ =           PShortString(PPointer(TJclAddr(VTable) + TJclAddr(vmtClassName))^)^);        if Result then          HandlerAt := FExcTab[I].Handler        else        begin          ParentVTable := TClass(VTable).ClassParent;          if ParentVTable = VTable then            VTable := nil          else            VTable := ParentVTable;        end;      end;      if Result then        Break;    end;  end  else  if Result then    HandlerAt := FCodeLocation  else    HandlerAt := nil;end;{$IFDEF OVERFLOWCHECKS_ON}{$OVERFLOWCHECKS ON}{$ENDIF OVERFLOWCHECKS_ON}//=== { TJclExceptFrameList } ================================================constructor TJclExceptFrameList.Create(AIgnoreLevels: Integer);begin  inherited Create;  FIgnoreLevels := AIgnoreLevels;  TraceExceptionFrames;end;function TJclExceptFrameList.AddFrame(AFrame: PExcFrame): TJclExceptFrame;begin  Result := TJclExceptFrame.Create(AFrame, AFrame^.Desc);  Add(Result);end;function TJclExceptFrameList.GetItems(Index: TJclListSize): TJclExceptFrame;begin  Result := TJclExceptFrame(Get(Index));end;procedure TJclExceptFrameList.TraceExceptionFrames;{$IFDEF CPU32}var  ExceptionPointer: PExcFrame;  Level: Integer;  ModulesList: TJclModuleInfoList;begin  Clear;  ModulesList := GlobalModulesList.CreateModulesList;  try    Level := 0;    ExceptionPointer := GetExceptionPointer;    while TJclAddr(ExceptionPointer) <> High(TJclAddr) do    begin      if (Level >= IgnoreLevels) and ValidCodeAddr(TJclAddr(ExceptionPointer^.Desc), ModulesList) then        AddFrame(ExceptionPointer);      Inc(Level);      ExceptionPointer := ExceptionPointer^.next;    end;  finally    GlobalModulesList.FreeModulesList(ModulesList);  end;end;{$ENDIF CPU32}{$IFDEF CPU64}begin  // TODO: 64-bit versionend;{$ENDIF CPU64}//=== Exception hooking ======================================================var  TrackingActiveCount: Integer;  IgnoredExceptions: TThreadList = nil;  IgnoredExceptionClassNames: TStringList = nil;  IgnoredExceptionClassNamesCritSect: TJclCriticalSection = nil;procedure AddIgnoredException(const ExceptionClass: TClass);begin  if Assigned(ExceptionClass) then  begin    if not Assigned(IgnoredExceptions) then      IgnoredExceptions := TThreadList.Create;    IgnoredExceptions.Add(ExceptionClass);  end;end;procedure AddIgnoredExceptionByName(const AExceptionClassName: string);begin  if AExceptionClassName <> '' then  begin    if not Assigned(IgnoredExceptionClassNamesCritSect) then      IgnoredExceptionClassNamesCritSect := TJclCriticalSection.Create;    if not Assigned(IgnoredExceptionClassNames) then    begin      IgnoredExceptionClassNames := TStringList.Create;      IgnoredExceptionClassNames.Duplicates := dupIgnore;      IgnoredExceptionClassNames.Sorted := True;    end;    IgnoredExceptionClassNamesCritSect.Enter;    try      IgnoredExceptionClassNames.Add(AExceptionClassName);    finally      IgnoredExceptionClassNamesCritSect.Leave;    end;  end;end;procedure RemoveIgnoredException(const ExceptionClass: TClass);var  ClassList: TList;begin  if Assigned(ExceptionClass) and Assigned(IgnoredExceptions) then  begin    ClassList := IgnoredExceptions.LockList;    try      ClassList.Remove(ExceptionClass);    finally      IgnoredExceptions.UnlockList;    end;  end;end;procedure RemoveIgnoredExceptionByName(const AExceptionClassName: string);var  Index: Integer;begin  if Assigned(IgnoredExceptionClassNames) and (AExceptionClassName <> '') then  begin    IgnoredExceptionClassNamesCritSect.Enter;    try      Index := IgnoredExceptionClassNames.IndexOf(AExceptionClassName);      if Index <> -1 then        IgnoredExceptionClassNames.Delete(Index);    finally      IgnoredExceptionClassNamesCritSect.Leave;    end;  end;end;function IsIgnoredException(const ExceptionClass: TClass): Boolean;var  ClassList: TList;  Index: Integer;begin  Result := False;  if Assigned(IgnoredExceptions) and not (stTraceAllExceptions in JclStackTrackingOptions) then  begin    ClassList := IgnoredExceptions.LockList;    try      for Index := 0 to ClassList.Count - 1 do        if ExceptionClass.InheritsFrom(TClass(ClassList.Items[Index])) then      begin        Result := True;        Break;      end;    finally      IgnoredExceptions.UnlockList;    end;  end;  if not Result and Assigned(IgnoredExceptionClassNames) and not (stTraceAllExceptions in JclStackTrackingOptions) then  begin    IgnoredExceptionClassNamesCritSect.Enter;    try      Result := IgnoredExceptionClassNames.IndexOf(ExceptionClass.ClassName) <> -1;      if not Result then        for Index := 0 to IgnoredExceptionClassNames.Count - 1 do          if InheritsFromByName(ExceptionClass, IgnoredExceptionClassNames[Index]) then          begin            Result := True;            Break;          end;    finally      IgnoredExceptionClassNamesCritSect.Leave;    end;  end;end;procedure AddModule(const ModuleName: string);begin  GlobalModulesList.AddModule(ModuleName);end;procedure DoExceptNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean;  BaseOfStack: Pointer);begin  if (TrackingActiveCount > 0) and (not (stDisableIfDebuggerAttached in JclStackTrackingOptions) or (not IsDebuggerAttached)) and    Assigned(ExceptObj) and (not IsIgnoredException(ExceptObj.ClassType)) and    (not (stMainThreadOnly in JclStackTrackingOptions) or (GetCurrentThreadId = MainThreadID)) then  begin    if stStack in JclStackTrackingOptions then      DoExceptionStackTrace(ExceptObj, ExceptAddr, OSException, BaseOfStack);    if stExceptFrame in JclStackTrackingOptions then      DoExceptFrameTrace;  end;end;function JclStartExceptionTracking: Boolean;begin  {Increment the tracking count only if exceptions are already being tracked or tracking can be started   successfully.}  if TrackingActiveCount = 0 then  begin    if JclHookExceptions and JclAddExceptNotifier(DoExceptNotify, npFirstChain) then    begin      TrackingActiveCount := 1;      Result := True;    end    else      Result := False;  end  else  begin    Inc(TrackingActiveCount);    Result := False;  end;end;function JclStopExceptionTracking: Boolean;begin  {If the current tracking count is 1, an attempt is made to stop tracking exceptions. If successful the   tracking count is set back to 0. If the current tracking count is > 1 it is simply decremented.}  if TrackingActiveCount = 1 then  begin    Result := JclRemoveExceptNotifier(DoExceptNotify) and JclUnhookExceptions;    if Result then      Dec(TrackingActiveCount);  end  else  begin    if TrackingActiveCount > 0 then      Dec(TrackingActiveCount);    Result := False;  end;end;function JclExceptionTrackingActive: Boolean;begin  Result := TrackingActiveCount > 0;end;function JclTrackExceptionsFromLibraries: Boolean;begin  Result := TrackingActiveCount > 0;  if Result then    JclInitializeLibrariesHookExcept;end;//=== Thread exception tracking support ======================================var  RegisteredThreadList: TJclDebugThreadList;function JclDebugThreadList: TJclDebugThreadList;begin  if RegisteredThreadList = nil then    RegisteredThreadList := TJclDebugThreadList.Create;  Result := RegisteredThreadList;end;type  TKernel32_CreateThread = function(SecurityAttributes: Pointer; StackSize: LongWord;    ThreadFunc: TThreadFunc; Parameter: Pointer;    CreationFlags: LongWord; var ThreadId: LongWord): Integer; stdcall;  TKernel32_ExitThread = procedure(ExitCode: Integer); stdcall;var  ThreadsHooked: Boolean;  Kernel32_CreateThread: TKernel32_CreateThread = nil;  Kernel32_ExitThread: TKernel32_ExitThread = nil;function HookedCreateThread(SecurityAttributes: Pointer; StackSize: LongWord;  ThreadFunc: TThreadFunc; Parameter: Pointer;  CreationFlags: LongWord; ThreadId: PLongWord): Integer; stdcall;var  LocalThreadId: LongWord;begin  Result := Kernel32_CreateThread(SecurityAttributes, StackSize, ThreadFunc, Parameter, CreationFlags, LocalThreadId);  if Result <> 0 then  begin    JclDebugThreadList.RegisterThreadID(LocalThreadId);    if ThreadId <> nil then    begin      ThreadId^ := LocalThreadId;    end;  end;end;procedure HookedExitThread(ExitCode: Integer); stdcall;begin  JclDebugThreadList.UnregisterThreadID(GetCurrentThreadID);  Kernel32_ExitThread(ExitCode);end;function JclHookThreads: Boolean;var  ProcAddrCache: Pointer;begin  if not ThreadsHooked then  begin    ProcAddrCache := GetProcAddress(GetModuleHandle(kernel32), 'CreateThread');    with TJclPeMapImgHooks do      Result := ReplaceImport(SystemBase, kernel32, ProcAddrCache, @HookedCreateThread);    if Result then    begin      @Kernel32_CreateThread := ProcAddrCache;      ProcAddrCache := GetProcAddress(GetModuleHandle(kernel32), 'ExitThread');      with TJclPeMapImgHooks do        Result := ReplaceImport(SystemBase, kernel32, ProcAddrCache, @HookedExitThread);      if Result then        @Kernel32_ExitThread := ProcAddrCache      else      with TJclPeMapImgHooks do        ReplaceImport(SystemBase, kernel32, @HookedCreateThread, @Kernel32_CreateThread);    end;    ThreadsHooked := Result;  end  else    Result := True;end;function JclUnhookThreads: Boolean;begin  if ThreadsHooked then  begin    with TJclPeMapImgHooks do    begin      ReplaceImport(SystemBase, kernel32, @HookedCreateThread, @Kernel32_CreateThread);      ReplaceImport(SystemBase, kernel32, @HookedExitThread, @Kernel32_ExitThread);    end;    Result := True;    ThreadsHooked := False;  end  else    Result := True;end;function JclThreadsHooked: Boolean;begin  Result := ThreadsHooked;end;//=== { TJclDebugThread } ====================================================constructor TJclDebugThread.Create(ASuspended: Boolean; const AThreadName: string);begin  FThreadName := AThreadName;  inherited Create(True);  JclDebugThreadList.RegisterThread(Self, AThreadName);  if not ASuspended then    {$IFDEF RTL210_UP}    Suspended := False;    {$ELSE ~RTL210_UP}    Resume;    {$ENDIF ~RTL210_UP}end;destructor TJclDebugThread.Destroy;begin  JclDebugThreadList.UnregisterThread(Self);  inherited Destroy;end;procedure TJclDebugThread.DoHandleException;begin  GlobalStackList.LockThreadID(ThreadID);  try    DoSyncHandleException;  finally    GlobalStackList.UnlockThreadID;  end;end;procedure TJclDebugThread.DoNotify;begin  JclDebugThreadList.DoSyncException(Self);end;procedure TJclDebugThread.DoSyncHandleException;begin  // Note: JclLastExceptStackList and JclLastExceptFrameList returns information  // for this Thread ID instead of MainThread ID here to allow use a common  // exception handling routine easily.  // Any other call of those JclLastXXX routines from another thread at the same  // time will return expected information for current Thread ID.  DoNotify;end;function TJclDebugThread.GetThreadInfo: string;begin  Result := JclDebugThreadList.ThreadInfos[ThreadID];end;procedure TJclDebugThread.HandleException(Sender: TObject);begin  FSyncException := Sender;  try    if not Assigned(FSyncException) then      FSyncException := Exception(ExceptObject);    if Assigned(FSyncException) and not IsIgnoredException(FSyncException.ClassType) then      Synchronize(DoHandleException);  finally    FSyncException := nil;  end;end;//=== { TJclDebugThreadList } ================================================type  TThreadAccess = class(TThread);constructor TJclDebugThreadList.Create;begin  FLock := TJclCriticalSection.Create;  FReadLock := TJclCriticalSection.Create;  FList := TObjectList.Create;  FSaveCreationStack := False;end;destructor TJclDebugThreadList.Destroy;begin  FreeAndNil(FList);  FreeAndNil(FLock);  FreeAndNil(FReadLock);  inherited Destroy;end;function TJclDebugThreadList.AddStackListToLocationInfoList(ThreadID: DWORD; AList: TJclLocationInfoList): Boolean;var  I: Integer;  List: TJclStackInfoList;begin  Result := False;  FReadLock.Enter;  try    I := IndexOfThreadID(ThreadID);    if (I <> -1) and Assigned(TJclDebugThreadInfo(FList[I]).StackList) then    begin      List := TJclDebugThreadInfo(FList[I]).StackList;      AList.AddStackInfoList(List);      Result := True;    end;  finally    FReadLock.Leave;  end;end;procedure TJclDebugThreadList.DoSyncException(Thread: TJclDebugThread);begin  if Assigned(FOnSyncException) then    FOnSyncException(Thread);end;procedure TJclDebugThreadList.DoSyncThreadRegistered;begin  if Assigned(FOnThreadRegistered) then    FOnThreadRegistered(FRegSyncThreadID);end;procedure TJclDebugThreadList.DoSyncThreadUnregistered;begin  if Assigned(FOnThreadUnregistered) then    FOnThreadUnregistered(FUnregSyncThreadID);end;procedure TJclDebugThreadList.DoThreadRegistered(Thread: TThread);begin  if Assigned(FOnThreadRegistered) then  begin    FRegSyncThreadID := Thread.ThreadID;    TThreadAccess(Thread).Synchronize(DoSyncThreadRegistered);  end;end;procedure TJclDebugThreadList.DoThreadUnregistered(Thread: TThread);begin  if Assigned(FOnThreadUnregistered) then  begin    FUnregSyncThreadID := Thread.ThreadID;    TThreadAccess(Thread).Synchronize(DoSyncThreadUnregistered);  end;end;function TJclDebugThreadList.GetThreadClassNames(ThreadID: DWORD): string;begin  Result := GetThreadValues(ThreadID, 1);end;function TJclDebugThreadList.GetThreadCreationTime(ThreadID: DWORD): TDateTime;var  I: Integer;begin  FReadLock.Enter;  try    I := IndexOfThreadID(ThreadID);    if I <> -1 then      Result := TJclDebugThreadInfo(FList[I]).CreationTime    else      Result := 0;  finally    FReadLock.Leave;  end;end;function TJclDebugThreadList.GetThreadIDCount: Integer;begin  FReadLock.Enter;  try    Result := FList.Count;  finally    FReadLock.Leave;  end;end;function TJclDebugThreadList.GetThreadHandle(Index: Integer): THandle;begin  FReadLock.Enter;  try    Result := TJclDebugThreadInfo(FList[Index]).ThreadHandle;  finally    FReadLock.Leave;  end;end;function TJclDebugThreadList.GetThreadID(Index: Integer): DWORD;begin  FReadLock.Enter;  try    Result := TJclDebugThreadInfo(FList[Index]).ThreadID;  finally    FReadLock.Leave;  end;end;function TJclDebugThreadList.GetThreadInfos(ThreadID: DWORD): string;begin  Result := GetThreadValues(ThreadID, 2);end;function TJclDebugThreadList.GetThreadNames(ThreadID: DWORD): string;begin  Result := GetThreadValues(ThreadID, 0);end;function TJclDebugThreadList.GetThreadParentID(ThreadID: DWORD): DWORD;var  I: Integer;begin  FReadLock.Enter;  try    I := IndexOfThreadID(ThreadID);    if I <> -1 then      Result := TJclDebugThreadInfo(FList[I]).ParentThreadID    else      Result := 0;  finally    FReadLock.Leave;  end;end;function TJclDebugThreadList.GetThreadValues(ThreadID: DWORD; Index: Integer): string;var  I: Integer;begin  FReadLock.Enter;  try    I := IndexOfThreadID(ThreadID);    if I <> -1 then    begin      case Index of        0:          Result := TJclDebugThreadInfo(FList[I]).ThreadName;        1:          Result := TJclDebugThreadInfo(FList[I]).ThreadClassName;        2:          Result := Format('%.8x [%s] "%s"', [ThreadID, TJclDebugThreadInfo(FList[I]).ThreadClassName,            TJclDebugThreadInfo(FList[I]).ThreadName]);      end;    end    else      Result := '';  finally    FReadLock.Leave;  end;end;function TJclDebugThreadList.IndexOfThreadID(ThreadID: DWORD): Integer;var  I: Integer;begin  Result := -1;  for I := FList.Count - 1 downto 0 do    if TJclDebugThreadInfo(FList[I]).ThreadID = ThreadID then    begin      Result := I;      Break;    end;end;procedure TJclDebugThreadList.InternalRegisterThread(Thread: TThread; ThreadID: DWORD; const ThreadName: string);var  I: Integer;  ThreadInfo: TJclDebugThreadInfo;begin  FLock.Enter;  try    I := IndexOfThreadID(ThreadID);    if I = -1 then    begin      FReadLock.Enter;      try        FList.Add(TJclDebugThreadInfo.Create(GetCurrentThreadId, ThreadID, FSaveCreationStack));        ThreadInfo := TJclDebugThreadInfo(FList.Last);        if Assigned(Thread) then        begin          ThreadInfo.ThreadHandle := Thread.Handle;          ThreadInfo.ThreadClassName := Thread.ClassName;        end        else        begin          ThreadInfo.ThreadHandle := 0;          ThreadInfo.ThreadClassName := '';        end;        ThreadInfo.ThreadName := ThreadName;      finally        FReadLock.Leave;      end;      if Assigned(Thread) then        DoThreadRegistered(Thread);    end;  finally    FLock.Leave;  end;end;procedure TJclDebugThreadList.InternalUnregisterThread(Thread: TThread; ThreadID: DWORD);var  I: Integer;begin  FLock.Enter;  try    I := IndexOfThreadID(ThreadID);    if I <> -1 then    begin      if Assigned(Thread) then        DoThreadUnregistered(Thread);      FReadLock.Enter;      try        FList.Delete(I);      finally        FReadLock.Leave;      end;    end;  finally    FLock.Leave;  end;end;procedure TJclDebugThreadList.RegisterThread(Thread: TThread; const ThreadName: string);begin  InternalRegisterThread(Thread, Thread.ThreadID, ThreadName);end;procedure TJclDebugThreadList.RegisterThreadID(AThreadID: DWORD; const ThreadName: string);begin  InternalRegisterThread(nil, AThreadID, ThreadName);end;procedure TJclDebugThreadList.UnregisterThread(Thread: TThread);begin  InternalUnregisterThread(Thread, Thread.ThreadID);end;procedure TJclDebugThreadList.UnregisterThreadID(AThreadID: DWORD);begin  InternalUnregisterThread(nil, AThreadID);end;//=== { TJclDebugThreadInfo } ================================================constructor TJclDebugThreadInfo.Create(AParentThreadID, AThreadID: DWORD; AStack: Boolean);begin  FCreationTime := Now;  FParentThreadID := AParentThreadID;  try  { TODO -oUSc : ... }//    FStackList := JclCreateStackList(True, 0, nil, True);//probably IgnoreLevels = 11    if AStack then      FStackList := TJclStackInfoList.Create(True, 0, nil, True, nil, nil)    else      FStackList := nil;  except    FStackList := nil;  end;  FThreadID := AThreadID;end;destructor TJclDebugThreadInfo.Destroy;begin  FStackList.Free;  inherited Destroy;end;//=== { TJclCustomThreadInfo } ===============================================constructor TJclCustomThreadInfo.Create;var  StackClass: TJclCustomLocationInfoListClass;begin  inherited Create;  StackClass := GetStackClass;  FCreationTime := 0;  FCreationStack := StackClass.Create;  FName := '';  FParentThreadID := 0;  FStack := StackClass.Create;  FThreadID := 0;  FValues := [];end;destructor TJclCustomThreadInfo.Destroy;begin  FCreationStack.Free;  FStack.Free;  inherited Destroy;end;procedure TJclCustomThreadInfo.AssignTo(Dest: TPersistent);begin  if Dest is TJclCustomThreadInfo then  begin    TJclCustomThreadInfo(Dest).FCreationTime := FCreationTime;    TJclCustomThreadInfo(Dest).FCreationStack.Assign(FCreationStack);    TJclCustomThreadInfo(Dest).FName := FName;    TJclCustomThreadInfo(Dest).FParentThreadID := FParentThreadID;    TJclCustomThreadInfo(Dest).FStack.Assign(FStack);    TJclCustomThreadInfo(Dest).FThreadID := FThreadID;    TJclCustomThreadInfo(Dest).FValues := FValues;  end  else    inherited AssignTo(Dest);end;function TJclCustomThreadInfo.GetStackClass: TJclCustomLocationInfoListClass;begin  Result := TJclLocationInfoList;end;//=== { TJclThreadInfo } =====================================================procedure TJclThreadInfo.Fill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions);begin  InternalFill(AThreadHandle, AThreadID, AGatherOptions, False);end;procedure TJclThreadInfo.FillFromExceptThread(AGatherOptions: TJclThreadInfoOptions);begin  InternalFill(0, GetCurrentThreadID, AGatherOptions, True);end;function TJclThreadInfo.GetAsString: string;var  ExceptInfo, ThreadName, ThreadInfoStr: string;begin  if tioIsMainThread in Values then    ThreadName := ' [MainThread]'  else  if tioName in Values then    ThreadName := Name  else    ThreadName := '';  ThreadInfoStr := '';  if tioCreationTime in Values then    ThreadInfoStr := ThreadInfoStr + Format(' CreationTime: %s', [DateTimeToStr(CreationTime)]);  if tioParentThreadID in Values then    ThreadInfoStr := ThreadInfoStr + Format(' ParentThreadID: %d', [ParentThreadID]);  ExceptInfo := Format('ThreadID: %d%s%s', [ThreadID, ThreadName, ThreadInfoStr]) + #13#10;  if tioStack in Values then    ExceptInfo := ExceptInfo + Stack.AsString;  if tioCreationStack in Values then    ExceptInfo := ExceptInfo + 'Created at:' + #13#10 + CreationStack.AsString + #13#10;  Result := ExceptInfo + #13#10;end;function TJclThreadInfo.GetStack(const AIndex: Integer): TJclLocationInfoList;begin  case AIndex of    1: Result := TJclLocationInfoList(FCreationStack);    2: Result := TJclLocationInfoList(FStack);    else      Result := nil;  end;end;function TJclThreadInfo.GetStackClass: TJclCustomLocationInfoListClass;begin  Result := TJclLocationInfoList;end;procedure TJclThreadInfo.InternalFill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions; AExceptThread: Boolean);var  Idx: Integer;  List: TJclStackInfoList;begin  if tioStack in AGatherOptions then  begin    if AExceptThread then      List := JclLastExceptStackList    else      List := JclCreateThreadStackTrace(True, AThreadHandle);    try      Stack.AddStackInfoList(List);      Values := Values + [tioStack];    except    { TODO -oUSc : ... }    end;  end;  ThreadID := AThreadID;  if tioIsMainThread in AGatherOptions then  begin    if MainThreadID = AThreadID then      Values := Values + [tioIsMainThread];  end;  if AGatherOptions * [tioName, tioCreationTime, tioParentThreadID, tioCreationStack] <> [] then    Idx := JclDebugThreadList.IndexOfThreadID(AThreadID)  else    Idx := -1;  if (tioName in AGatherOptions) and (Idx <> -1) then  begin    Name := JclDebugThreadList.ThreadNames[AThreadID];    Values := Values + [tioName];  end;  if (tioCreationTime in AGatherOptions) and (Idx <> -1) then  begin    CreationTime := JclDebugThreadList.ThreadCreationTime[AThreadID];    Values := Values + [tioCreationTime];  end;  if (tioParentThreadID in AGatherOptions) and (Idx <> -1) then  begin    ParentThreadID := JclDebugThreadList.ThreadParentIDs[AThreadID];    Values := Values + [tioParentThreadID];  end;  if (tioCreationStack in AGatherOptions) and (Idx <> -1) then  begin    try      if JclDebugThreadList.AddStackListToLocationInfoList(AThreadID, CreationStack) then        Values := Values + [tioCreationStack];    except      { TODO -oUSc : ... }    end;  end;end;//=== { TJclThreadInfoList } =================================================constructor TJclThreadInfoList.Create;begin  inherited Create;  FItems := TObjectList.Create;  FGatherOptions := [tioIsMainThread, tioName, tioCreationTime, tioParentThreadID, tioStack, tioCreationStack];end;destructor TJclThreadInfoList.Destroy;begin  FItems.Free;  inherited Destroy;end;function TJclThreadInfoList.Add: TJclThreadInfo;begin  FItems.Add(TJclThreadInfo.Create);  Result := TJclThreadInfo(FItems.Last);end;procedure TJclThreadInfoList.AssignTo(Dest: TPersistent);var  I: Integer;begin  if Dest is TJclThreadInfoList then  begin    TJclThreadInfoList(Dest).Clear;    for I := 0 to Count - 1 do      TJclThreadInfoList(Dest).Add.Assign(Items[I]);    TJclThreadInfoList(Dest).GatherOptions := FGatherOptions;  end  else    inherited AssignTo(Dest);end;procedure TJclThreadInfoList.Clear;begin  FItems.Clear;end;function TJclThreadInfoList.GetAsString: string;var  I: Integer;begin  Result := '';  for I := 0 to Count - 1 do    Result := Result + Items[I].AsString + #13#10;end;procedure TJclThreadInfoList.Gather(AExceptThreadID: DWORD);begin  InternalGather([], [AExceptThreadID]);end;procedure TJclThreadInfoList.GatherExclude(AThreadIDs: array of DWORD);begin  InternalGather([], AThreadIDs);end;procedure TJclThreadInfoList.GatherInclude(AThreadIDs: array of DWORD);begin  InternalGather(AThreadIDs, []);end;function TJclThreadInfoList.GetCount: Integer;begin  Result := FItems.Count;end;function TJclThreadInfoList.GetItems(AIndex: Integer): TJclThreadInfo;begin  Result := TJclThreadInfo(FItems[AIndex]);end;procedure TJclThreadInfoList.InternalGather(AIncludeThreadIDs, AExcludeThreadIDs: array of DWORD);  function OpenThread(ThreadID: DWORD): THandle;  type    TOpenThreadFunc = function(DesiredAccess: DWORD; InheritHandle: BOOL; ThreadID: DWORD): THandle; stdcall;  const    THREAD_SUSPEND_RESUME    = $0002;    THREAD_GET_CONTEXT       = $0008;    THREAD_QUERY_INFORMATION = $0040;  var    Kernel32Lib: THandle;    OpenThreadFunc: TOpenThreadFunc;  begin    Result := 0;    Kernel32Lib := GetModuleHandle(kernel32);    if Kernel32Lib <> 0 then    begin      // OpenThread only exists since Windows ME      OpenThreadFunc := GetProcAddress(Kernel32Lib, 'OpenThread');      if Assigned(OpenThreadFunc) then        Result := OpenThreadFunc(THREAD_SUSPEND_RESUME or THREAD_GET_CONTEXT or THREAD_QUERY_INFORMATION, False, ThreadID);    end;  end;  function SearchThreadInArray(AThreadIDs: array of DWORD; AThreadID: DWORD): Boolean;  var    I: Integer;  begin    Result := False;    if Length(AThreadIDs) > 0 then      for I := Low(AThreadIDs) to High(AThreadIDs) do        if AThreadIDs[I] = AThreadID then        begin          Result := True;          Break;        end;  end;var  SnapProcHandle: THandle;  ThreadEntry: TThreadEntry32;  NextThread: Boolean;  ThreadIDList, ThreadHandleList: TList;  I: Integer;  PID, TID: DWORD;  ThreadHandle: THandle;  ThreadInfo: TJclThreadInfo;begin  ThreadIDList := TList.Create;  ThreadHandleList := TList.Create;  try    SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);    if SnapProcHandle <> INVALID_HANDLE_VALUE then    try      PID := GetCurrentProcessId;      ThreadEntry.dwSize := SizeOf(ThreadEntry);      NextThread := Thread32First(SnapProcHandle, ThreadEntry);      while NextThread do      begin        if ThreadEntry.th32OwnerProcessID = PID then        begin          if SearchThreadInArray(AIncludeThreadIDs, ThreadEntry.th32ThreadID) or            not SearchThreadInArray(AExcludeThreadIDs, ThreadEntry.th32ThreadID) then            ThreadIDList.Add(Pointer(ThreadEntry.th32ThreadID));        end;        NextThread := Thread32Next(SnapProcHandle, ThreadEntry);      end;    finally      CloseHandle(SnapProcHandle);    end;    for I := 0 to ThreadIDList.Count - 1 do    begin      ThreadHandle := OpenThread(TJclAddr(ThreadIDList[I]));      ThreadHandleList.Add(Pointer(ThreadHandle));      if ThreadHandle <> 0 then        SuspendThread(ThreadHandle);    end;    try      for I := 0 to ThreadIDList.Count - 1 do      begin        ThreadHandle := THandle(ThreadHandleList[I]);        TID := TJclAddr(ThreadIDList[I]);        ThreadInfo := Add;        ThreadInfo.Fill(ThreadHandle, TID, FGatherOptions);      end;    finally      for I := 0 to ThreadHandleList.Count - 1 do        if ThreadHandleList[I] <> nil then        begin          ThreadHandle := THandle(ThreadHandleList[I]);          ResumeThread(ThreadHandle);          CloseHandle(ThreadHandle);        end;    end;  finally    ThreadIDList.Free;    ThreadHandleList.Free;  end;end;//== Miscellanuous ==========================================================={$IFDEF MSWINDOWS}{$IFNDEF WINSCP}function EnableCrashOnCtrlScroll(const Enable: Boolean): Boolean;const  CrashCtrlScrollKey = 'SYSTEM\CurrentControlSet\Services\i8042prt\Parameters';  CrashCtrlScrollName = 'CrashOnCtrlScroll';var  Enabled: Integer;begin  Enabled := 0;  if Enable then    Enabled := 1;  RegWriteInteger(HKEY_LOCAL_MACHINE, CrashCtrlScrollKey, CrashCtrlScrollName, Enabled);  Result := RegReadInteger(HKEY_LOCAL_MACHINE, CrashCtrlScrollKey, CrashCtrlScrollName) = Enabled;end;{$ENDIF ~WINSCP}function IsDebuggerAttached: Boolean;var  IsDebuggerPresent: function: Boolean; stdcall;  KernelHandle: THandle;  P: Pointer;begin  KernelHandle := GetModuleHandle(kernel32);  @IsDebuggerPresent := GetProcAddress(KernelHandle, 'IsDebuggerPresent');  if @IsDebuggerPresent <> nil then  begin    // Win98+ / NT4+    Result := IsDebuggerPresent  end  else  begin    // Win9x uses thunk pointer outside the module when under a debugger    P := GetProcAddress(KernelHandle, 'GetProcAddress');    Result := TJclAddr(P) < KernelHandle;  end;end;function IsHandleValid(Handle: THandle): Boolean;var  Duplicate: THandle;  Flags: DWORD;begin  if IsWinNT then  begin    Flags := 0;    Result := GetHandleInformation(Handle, Flags);  end  else    Result := False;  if not Result then  begin    // DuplicateHandle is used as an additional check for those object types not    // supported by GetHandleInformation (e.g. according to the documentation,    // GetHandleInformation doesn't support window stations and desktop although    // tests show that it does). GetHandleInformation is tried first because its    // much faster. Additionally GetHandleInformation is only supported on NT...    Result := DuplicateHandle(GetCurrentProcess, Handle, GetCurrentProcess,      @Duplicate, 0, False, DUPLICATE_SAME_ACCESS);    if Result then      Result := CloseHandle(Duplicate);  end;end;{$ENDIF MSWINDOWS}{$IFDEF HAS_EXCEPTION_STACKTRACE}type  PJclStackInfoRec = ^TJclStackInfoRec;  TJclStackInfoRec = record    Stack: TJclStackInfoList;    Stacktrace: string;  end;procedure ResolveStackInfoRec(Info: PJclStackInfoRec);var  Str: TStringList;begin  if (Info <> nil) and (Info.Stack <> nil) then  begin    Str := TStringList.Create;    try      Info.Stack.AddToStrings(Str,        estoIncludeModuleName in JclExceptionStacktraceOptions,        estoIncludeAdressOffset in JclExceptionStacktraceOptions,        estoIncludeStartProcLineOffset in JclExceptionStacktraceOptions,        estoIncludeVAddress in JclExceptionStacktraceOptions      );      FreeAndNil(Info.Stack);      Info.Stacktrace := Str.Text;    finally      FreeAndNil(Str);    end;  end;end;procedure CleanUpStackInfo(Info: Pointer);begin  if Info <> nil then  begin    PJclStackInfoRec(Info).Stack.Free;    Dispose(PJclStackInfoRec(Info));  end;end;{$STACKFRAMES ON}// We use the StackFrame's Base-Pointer to skip all local variables from this functionfunction GetExceptionStackInfo(P: PExceptionRecord): Pointer;const  cDelphiException = $0EEDFADE;  cSetThreadNameException = $406D1388;var  Stack: TJclStackInfoList;  Info: PJclStackInfoRec;  RawMode: Boolean;  Delayed: Boolean;  IgnoreLevels: Integer;begin  if P^.ExceptionCode = cSetThreadNameException then  begin    Result := nil;    Exit;  end;  RawMode := stRawMode in JclStackTrackingOptions;  Delayed := stDelayedTrace in JclStackTrackingOptions;  IgnoreLevels := 0;  if RawMode then  begin    // Skip RaiseExceptionObject, System.@RaiseExcept and the function causing the exception.    // The causing function is added again as the first stack item through P.ExceptionAddress.    if (P.ExceptionAddress <> nil) and (P^.ExceptionCode = cDelphiException) then      Inc(IgnoreLevels, 3)    else      Inc(IgnoreLevels, 2);  end;  if P^.ExceptionCode = cDelphiException then  begin    if (P^.ExceptObject <> nil) and (Exception(P.ExceptObject).StackInfo <> nil) then    begin      // This method is called twice for the same exception object if the user calls      // AcquireExceptionObject and then throws this exception again. In this case the      // StackInfo is already allocated and by overwriting it we produce a memory leak.      // Example: "E := AcquireExceptionObject; raise E;"      Result := Exception(P.ExceptObject).StackInfo;      Exit;    end;    if (P^.ExceptObject <> nil) and       not (stTraceAllExceptions in JclStackTrackingOptions) and       IsIgnoredException(TObject(P^.ExceptObject).ClassType) then    begin      Result := nil;      Exit;    end;    Stack := TJclStackInfoList.Create(RawMode, IgnoreLevels, P^.ExceptAddr, Delayed, GetFramePointer); // Don't add it to the GlobalStackList  end  else    Stack := TJclStackInfoList.Create(RawMode, IgnoreLevels, P^.ExceptionAddress, Delayed, GetFramePointer); // Don't add it to the GlobalStackList  New(Info);  Info.Stack := Stack;  if stImmediateExceptionStacktraceResolving in JclStackTrackingOptions then  begin    try      ResolveStackInfoRec(Info);    except      CleanUpStackInfo(Info);      Info := nil;    end;  end;  Result := Info;end;{$IFDEF STACKFRAMES_ON}  {$STACKFRAMES ON}{$ENDIF STACKFRAMES_ON}function GetStackInfoString(Info: Pointer): string;var  Rec: PJclStackInfoRec;begin  Rec := Info;  if Rec <> nil then  begin    if Rec.Stack <> nil then      ResolveStackInfoRec(Rec);    Result := Rec.Stacktrace;  end  else    Result := '';end;procedure SetupExceptionProcs;begin  if not Assigned(Exception.GetExceptionStackInfoProc) then  begin    Exception.GetExceptionStackInfoProc := GetExceptionStackInfo;    Exception.GetStackInfoStringProc := GetStackInfoString;    Exception.CleanUpStackInfoProc := CleanUpStackInfo;  end;end;procedure ResetExceptionProcs;begin  if @Exception.GetExceptionStackInfoProc = @GetExceptionStackInfo then  begin    Exception.GetExceptionStackInfoProc := nil;    Exception.GetStackInfoStringProc := nil;    Exception.CleanUpStackInfoProc := nil;  end;end;{$ENDIF HAS_EXCEPTION_STACKTRACE}procedure InitHexMap;var  Ch: AnsiChar;begin  FillChar(HexMap, SizeOf(HexMap), $80);  for Ch := '0' to '9' do    HexMap[Ch] := Ord(Ch) - Ord('0');  for Ch := 'a' to 'f' do    HexMap[Ch] := Ord(Ch) - (Ord('a') - 10);  for Ch := 'A' to 'F' do    HexMap[Ch] := Ord(Ch) - (Ord('A') - 10);end;procedure FreeJclDebugGlobals;begin  {$IFDEF HAS_EXCEPTION_STACKTRACE}  ResetExceptionProcs;  {$ENDIF HAS_EXCEPTION_STACKTRACE}  FreeAndNil(RegisteredThreadList);  FreeAndNil(DebugInfoList);  FreeAndNil(GlobalStackList);  FreeAndNil(GlobalModulesList);  FreeAndNil(DebugInfoCritSect);  FreeAndNil(InfoSourceClassList);  FreeAndNil(IgnoredExceptions);  FreeAndNil(IgnoredExceptionClassNames);  FreeAndNil(IgnoredExceptionClassNamesCritSect);  TJclDebugInfoSymbols.CleanupDebugSymbols;end;initialization  InitHexMap;  DebugInfoCritSect := TJclCriticalSection.Create;  GlobalModulesList := TJclGlobalModulesList.Create;  GlobalStackList := TJclGlobalStackList.Create;  AddIgnoredException(EAbort);  {$IFDEF UNITVERSIONING}  RegisterUnitVersion(HInstance, UnitVersioning);  {$ENDIF UNITVERSIONING}  {$IFDEF HAS_EXCEPTION_STACKTRACE}  SetupExceptionProcs;  {$ENDIF HAS_EXCEPTION_STACKTRACE}finalization  {$IFDEF UNITVERSIONING}  UnregisterUnitVersion(HInstance);  {$ENDIF UNITVERSIONING}  { TODO -oPV -cInvestigate : Calling JclStopExceptionTracking causes linking of various classes to    the code without a real need. Although there doesn't seem to be a way to unhook exceptions    safely because we need to be covered by JclHookExcept.Notifiers critical section }  JclStopExceptionTracking;  GlobalStackList.Clear;  JclDebugFinalized := True;  if GlobalStackListLiveCount = 0 then    FreeJclDebugGlobals;end.
 |