JclDebug.pas 221 KB

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