JclDebug.pas 221 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985
  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. begin
  4616. Result := JclCreateStackList(Raw, -1, Pointer(AlignedContext^.Eip), False, Pointer(AlignedContext^.Ebp),
  4617. Pointer(GetThreadTopOfStack(ThreadHandle)));
  4618. end;
  4619. {$ENDIF CPU32}
  4620. {$IFDEF CPU64}
  4621. if GetThreadContext(ThreadHandle, AlignedContext^) then
  4622. Result := JclCreateStackList(Raw, -1, Pointer(AlignedContext^.Rip), False, Pointer(AlignedContext^.Rbp),
  4623. Pointer(GetThreadTopOfStack(ThreadHandle)));
  4624. {$ENDIF CPU64}
  4625. finally
  4626. FreeMem(ContextMemory);
  4627. end;
  4628. end;
  4629. function JclCreateThreadStackTraceFromID(Raw: Boolean; ThreadID: DWORD): TJclStackInfoList;
  4630. type
  4631. TOpenThreadFunc = function(DesiredAccess: DWORD; InheritHandle: BOOL; ThreadID: DWORD): THandle; stdcall;
  4632. const
  4633. THREAD_GET_CONTEXT = $0008;
  4634. THREAD_QUERY_INFORMATION = $0040;
  4635. var
  4636. Kernel32Lib, ThreadHandle: THandle;
  4637. OpenThreadFunc: TOpenThreadFunc;
  4638. begin
  4639. Result := nil;
  4640. Kernel32Lib := GetModuleHandle(kernel32);
  4641. if Kernel32Lib <> 0 then
  4642. begin
  4643. // OpenThread only exists since Windows ME
  4644. OpenThreadFunc := GetProcAddress(Kernel32Lib, 'OpenThread');
  4645. if Assigned(OpenThreadFunc) then
  4646. begin
  4647. ThreadHandle := OpenThreadFunc(THREAD_GET_CONTEXT or THREAD_QUERY_INFORMATION, False, ThreadID);
  4648. if ThreadHandle <> 0 then
  4649. try
  4650. Result := JclCreateThreadStackTrace(Raw, ThreadHandle);
  4651. finally
  4652. CloseHandle(ThreadHandle);
  4653. end;
  4654. end;
  4655. end;
  4656. end;
  4657. //=== { TJclStackInfoItem } ==================================================
  4658. function TJclStackInfoItem.GetCallerAddr: Pointer;
  4659. begin
  4660. Result := Pointer(FStackInfo.CallerAddr);
  4661. end;
  4662. function TJclStackInfoItem.GetLogicalAddress: TJclAddr;
  4663. begin
  4664. Result := FStackInfo.CallerAddr - TJclAddr(ModuleFromAddr(CallerAddr));
  4665. end;
  4666. //=== { TJclStackInfoList } ==================================================
  4667. constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;
  4668. AFirstCaller: Pointer);
  4669. begin
  4670. Create(ARaw, AIgnoreLevels, AFirstCaller, False, nil, nil);
  4671. end;
  4672. constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;
  4673. AFirstCaller: Pointer; ADelayedTrace: Boolean);
  4674. begin
  4675. Create(ARaw, AIgnoreLevels, AFirstCaller, ADelayedTrace, nil, nil);
  4676. end;
  4677. constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;
  4678. AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack: Pointer);
  4679. begin
  4680. Create(ARaw, AIgnoreLevels, AFirstCaller, ADelayedTrace, ABaseOfStack, nil);
  4681. end;
  4682. constructor TJclStackInfoList.Create(ARaw: Boolean; AIgnoreLevels: Integer;
  4683. AFirstCaller: Pointer; ADelayedTrace: Boolean; ABaseOfStack, ATopOfStack: Pointer);
  4684. var
  4685. Item: TJclStackInfoItem;
  4686. begin
  4687. inherited Create;
  4688. FIgnoreLevels := AIgnoreLevels;
  4689. FDelayedTrace := ADelayedTrace;
  4690. FRaw := ARaw;
  4691. BaseOfStack := TJclAddr(ABaseOfStack);
  4692. FStackOffset := 0;
  4693. FFramePointer := ABaseOfStack;
  4694. if ATopOfStack = nil then
  4695. TopOfStack := GetStackTop
  4696. else
  4697. TopOfStack := TJclAddr(ATopOfStack);
  4698. FModuleInfoList := GlobalModulesList.CreateModulesList;
  4699. if AFirstCaller <> nil then
  4700. begin
  4701. Item := TJclStackInfoItem.Create;
  4702. Item.FStackInfo.CallerAddr := TJclAddr(AFirstCaller);
  4703. Add(Item);
  4704. end;
  4705. {$IFDEF CPU32}
  4706. if DelayedTrace then
  4707. DelayStoreStack
  4708. else
  4709. if Raw then
  4710. TraceStackRaw
  4711. else
  4712. TraceStackFrames;
  4713. {$ENDIF CPU32}
  4714. {$IFDEF CPU64}
  4715. CaptureBackTrace;
  4716. {$ENDIF CPU64}
  4717. end;
  4718. destructor TJclStackInfoList.Destroy;
  4719. begin
  4720. if Assigned(FStackData) then
  4721. FreeMem(FStackData);
  4722. GlobalModulesList.FreeModulesList(FModuleInfoList);
  4723. inherited Destroy;
  4724. end;
  4725. {$IFDEF CPU64}
  4726. procedure TJclStackInfoList.CaptureBackTrace;
  4727. const
  4728. InternalSkipFrames = 1; // skip this method
  4729. var
  4730. BackTrace: array [0..127] of Pointer;
  4731. MaxFrames: Integer;
  4732. Hash: DWORD;
  4733. I: Integer;
  4734. StackInfo: TStackInfo;
  4735. CapturedFramesCount: Word;
  4736. begin
  4737. if JclCheckWinVersion(6, 0) then
  4738. MaxFrames := Length(BackTrace)
  4739. else
  4740. begin
  4741. // For XP and 2003 sum of FramesToSkip and FramesToCapture must be lower than 63
  4742. MaxFrames := 62 - InternalSkipFrames;
  4743. end;
  4744. ResetMemory(BackTrace, SizeOf(BackTrace));
  4745. CapturedFramesCount := CaptureStackBackTrace(InternalSkipFrames, MaxFrames, @BackTrace, Hash);
  4746. ResetMemory(StackInfo, SizeOf(StackInfo));
  4747. for I := 0 to CapturedFramesCount - 1 do
  4748. begin
  4749. StackInfo.CallerAddr := TJclAddr(BackTrace[I]);
  4750. StackInfo.Level := I;
  4751. StoreToList(StackInfo); // skips all frames with a level less than "IgnoreLevels"
  4752. end;
  4753. end;
  4754. {$ENDIF CPU64}
  4755. procedure TJclStackInfoList.ForceStackTracing;
  4756. begin
  4757. if DelayedTrace and Assigned(FStackData) and not FInStackTracing then
  4758. begin
  4759. FInStackTracing := True;
  4760. try
  4761. if Raw then
  4762. TraceStackRaw
  4763. else
  4764. TraceStackFrames;
  4765. if FCorrectOnAccess then
  4766. CorrectExceptStackListTop(Self, FSkipFirstItem);
  4767. finally
  4768. FInStackTracing := False;
  4769. FDelayedTrace := False;
  4770. end;
  4771. end;
  4772. end;
  4773. function TJclStackInfoList.GetCount: Integer;
  4774. begin
  4775. ForceStackTracing;
  4776. Result := inherited Count;
  4777. end;
  4778. procedure TJclStackInfoList.CorrectOnAccess(ASkipFirstItem: Boolean);
  4779. begin
  4780. FCorrectOnAccess := True;
  4781. FSkipFirstItem := ASkipFirstItem;
  4782. end;
  4783. procedure TJclStackInfoList.AddToStrings(Strings: TStrings; IncludeModuleName, IncludeAddressOffset,
  4784. IncludeStartProcLineOffset, IncludeVAddress: Boolean);
  4785. var
  4786. I: Integer;
  4787. S: string;
  4788. begin
  4789. ForceStackTracing;
  4790. Strings.BeginUpdate;
  4791. try
  4792. for I := 0 to Count - 1 do
  4793. begin
  4794. S := GetLocationInfoStr(Items[I].CallerAddr, IncludeModuleName, IncludeAddressOffset,
  4795. IncludeStartProcLineOffset, IncludeVAddress);
  4796. Strings.Add(S);
  4797. end;
  4798. finally
  4799. Strings.EndUpdate;
  4800. end;
  4801. end;
  4802. function TJclStackInfoList.GetItems(Index: Integer): TJclStackInfoItem;
  4803. begin
  4804. ForceStackTracing;
  4805. Result := TJclStackInfoItem(Get(Index));
  4806. end;
  4807. function TJclStackInfoList.NextStackFrame(var StackFrame: PStackFrame; var StackInfo: TStackInfo): Boolean;
  4808. var
  4809. CallInstructionSize: Cardinal;
  4810. StackFrameCallerFrame, NewFrame: TJclAddr;
  4811. StackFrameCallerAddr: TJclAddr;
  4812. begin
  4813. // Only report this stack frame into the StockInfo structure
  4814. // if the StackFrame pointer, the frame pointer and the return address on the stack
  4815. // are valid addresses
  4816. StackFrameCallerFrame := StackInfo.CallerFrame;
  4817. while ValidStackAddr(TJclAddr(StackFrame)) do
  4818. begin
  4819. // CallersEBP above the previous CallersEBP
  4820. NewFrame := StackFrame^.CallerFrame;
  4821. if NewFrame <= StackFrameCallerFrame then
  4822. Break;
  4823. StackFrameCallerFrame := NewFrame;
  4824. // CallerAddr within current process space, code segment etc.
  4825. // CallerFrame within current thread stack. Added Mar 12 2002 per Hallvard's suggestion
  4826. StackFrameCallerAddr := StackFrame^.CallerAddr;
  4827. if ValidCodeAddr(StackFrameCallerAddr, FModuleInfoList) and ValidStackAddr(StackFrameCallerFrame + FStackOffset) then
  4828. begin
  4829. Inc(StackInfo.Level);
  4830. StackInfo.StackFrame := StackFrame;
  4831. StackInfo.ParamPtr := PDWORD_PTRArray(TJclAddr(StackFrame) + SizeOf(TStackFrame));
  4832. if StackFrameCallerFrame > StackInfo.CallerFrame then
  4833. StackInfo.CallerFrame := StackFrameCallerFrame
  4834. else
  4835. // the frame pointer points to an address that is below
  4836. // the last frame pointer, so it must be invalid
  4837. Break;
  4838. // Calculate the address of caller by subtracting the CALL instruction size (if possible)
  4839. if ValidCallSite(StackFrameCallerAddr, CallInstructionSize) then
  4840. StackInfo.CallerAddr := StackFrameCallerAddr - CallInstructionSize
  4841. else
  4842. StackInfo.CallerAddr := StackFrameCallerAddr;
  4843. // the stack may be messed up in big projects, avoid overflow in arithmetics
  4844. if StackFrameCallerFrame < TJclAddr(StackFrame) then
  4845. Break;
  4846. StackInfo.DumpSize := StackFrameCallerFrame - TJclAddr(StackFrame);
  4847. StackInfo.ParamSize := (StackInfo.DumpSize - SizeOf(TStackFrame)) div 4;
  4848. if PStackFrame(StackFrame^.CallerFrame) = StackFrame then
  4849. Break;
  4850. // Step to the next stack frame by following the frame pointer
  4851. StackFrame := PStackFrame(StackFrameCallerFrame + FStackOffset);
  4852. Result := True;
  4853. Exit;
  4854. end;
  4855. // Step to the next stack frame by following the frame pointer
  4856. StackFrame := PStackFrame(StackFrameCallerFrame + FStackOffset);
  4857. end;
  4858. Result := False;
  4859. end;
  4860. procedure TJclStackInfoList.StoreToList(const StackInfo: TStackInfo);
  4861. var
  4862. Item: TJclStackInfoItem;
  4863. begin
  4864. if ((IgnoreLevels = -1) and (StackInfo.Level > 0)) or
  4865. (StackInfo.Level > (IgnoreLevels + 1)) then
  4866. begin
  4867. Item := TJclStackInfoItem.Create;
  4868. Item.FStackInfo := StackInfo;
  4869. Add(Item);
  4870. end;
  4871. end;
  4872. procedure TJclStackInfoList.TraceStackFrames;
  4873. var
  4874. StackFrame: PStackFrame;
  4875. StackInfo: TStackInfo;
  4876. begin
  4877. Capacity := 32; // reduce ReallocMem calls, must be > 1 because the caller's EIP register is already in the list
  4878. // Start at level 0
  4879. StackInfo.Level := 0;
  4880. StackInfo.CallerFrame := 0;
  4881. if DelayedTrace then
  4882. // Get the current stack frame from the frame register
  4883. StackFrame := FFramePointer
  4884. else
  4885. begin
  4886. // We define the bottom of the valid stack to be the current ESP pointer
  4887. if BaseOfStack = 0 then
  4888. BaseOfStack := TJclAddr(GetFramePointer);
  4889. // Get a pointer to the current bottom of the stack
  4890. StackFrame := PStackFrame(BaseOfStack);
  4891. end;
  4892. // We define the bottom of the valid stack to be the current frame Pointer
  4893. // There is a TIB field called pvStackUserBase, but this includes more of the
  4894. // stack than what would define valid stack frames.
  4895. BaseOfStack := TJclAddr(StackFrame) - 1;
  4896. // Loop over and report all valid stackframes
  4897. while NextStackFrame(StackFrame, StackInfo) and (inherited Count <> MaxStackTraceItems) do
  4898. StoreToList(StackInfo);
  4899. end;
  4900. function SearchForStackPtrManipulation(StackPtr: Pointer; Proc: Pointer): Pointer;
  4901. {$IFDEF SUPPORTS_INLINE}
  4902. inline;
  4903. {$ENDIF SUPPORTS_INLINE}
  4904. {var
  4905. Addr: PByteArray;}
  4906. begin
  4907. { Addr := Proc;
  4908. while (Addr <> nil) and (DWORD_PTR(Addr) > DWORD_PTR(Proc) - $100) and not IsBadReadPtr(Addr, 6) do
  4909. begin
  4910. if (Addr[0] = $55) and // push ebp
  4911. (Addr[1] = $8B) and (Addr[2] = $EC) then // mov ebp,esp
  4912. begin
  4913. if (Addr[3] = $83) and (Addr[4] = $C4) then // add esp,c8
  4914. begin
  4915. Result := Pointer(INT_PTR(StackPtr) - ShortInt(Addr[5]));
  4916. Exit;
  4917. end;
  4918. Break;
  4919. end;
  4920. if (Addr[0] = $C2) and // ret $xxxx
  4921. (((Addr[3] = $90) and (Addr[4] = $90) and (Addr[5] = $90)) or // nop
  4922. ((Addr[3] = $CC) and (Addr[4] = $CC) and (Addr[5] = $CC))) then // int 3
  4923. Break;
  4924. if (Addr[0] = $C3) and // ret
  4925. (((Addr[1] = $90) and (Addr[2] = $90) and (Addr[3] = $90)) or // nop
  4926. ((Addr[1] = $CC) and (Addr[2] = $CC) and (Addr[3] = $CC))) then // int 3
  4927. Break;
  4928. if (Addr[0] = $E9) and // jmp rel-far
  4929. (((Addr[5] = $90) and (Addr[6] = $90) and (Addr[7] = $90)) or // nop
  4930. ((Addr[5] = $CC) and (Addr[6] = $CC) and (Addr[7] = $CC))) then // int 3
  4931. Break;
  4932. if (Addr[0] = $EB) and // jmp rel-near
  4933. (((Addr[2] = $90) and (Addr[3] = $90) and (Addr[4] = $90)) or // nop
  4934. ((Addr[2] = $CC) and (Addr[3] = $CC) and (Addr[4] = $CC))) then // int 3
  4935. Break;
  4936. Dec(DWORD_TR(Addr));
  4937. end;}
  4938. Result := StackPtr;
  4939. end;
  4940. procedure TJclStackInfoList.TraceStackRaw;
  4941. var
  4942. StackInfo: TStackInfo;
  4943. StackPtr: PJclAddr;
  4944. PrevCaller: TJclAddr;
  4945. CallInstructionSize: Cardinal;
  4946. StackTop: TJclAddr;
  4947. begin
  4948. Capacity := 32; // reduce ReallocMem calls, must be > 1 because the caller's EIP register is already in the list
  4949. if DelayedTrace then
  4950. begin
  4951. if not Assigned(FStackData) then
  4952. Exit;
  4953. StackPtr := PJclAddr(FStackData);
  4954. end
  4955. else
  4956. begin
  4957. // We define the bottom of the valid stack to be the current ESP pointer
  4958. if BaseOfStack = 0 then
  4959. BaseOfStack := TJclAddr(GetStackPointer);
  4960. // Get a pointer to the current bottom of the stack
  4961. StackPtr := PJclAddr(BaseOfStack);
  4962. end;
  4963. StackTop := TopOfStack;
  4964. if Count > 0 then
  4965. StackPtr := SearchForStackPtrManipulation(StackPtr, Pointer(Items[0].StackInfo.CallerAddr));
  4966. // We will not be able to fill in all the fields in the StackInfo record,
  4967. // so just blank it all out first
  4968. ResetMemory(StackInfo, SizeOf(StackInfo));
  4969. // Clear the previous call address
  4970. PrevCaller := 0;
  4971. // Loop through all of the valid stack space
  4972. while (TJclAddr(StackPtr) < StackTop) and (inherited Count <> MaxStackTraceItems) do
  4973. begin
  4974. // If the current DWORD on the stack refers to a valid call site...
  4975. if ValidCallSite(StackPtr^, CallInstructionSize) and (StackPtr^ <> PrevCaller) then
  4976. begin
  4977. // then pick up the callers address
  4978. StackInfo.CallerAddr := StackPtr^ - CallInstructionSize;
  4979. // remember to callers address so that we don't report it repeatedly
  4980. PrevCaller := StackPtr^;
  4981. // increase the stack level
  4982. Inc(StackInfo.Level);
  4983. // then report it back to our caller
  4984. StoreToList(StackInfo);
  4985. StackPtr := SearchForStackPtrManipulation(StackPtr, Pointer(StackInfo.CallerAddr));
  4986. end;
  4987. // Look at the next DWORD on the stack
  4988. Inc(StackPtr);
  4989. end;
  4990. if Assigned(FStackData) then
  4991. begin
  4992. FreeMem(FStackData);
  4993. FStackData := nil;
  4994. end;
  4995. end;
  4996. {$IFDEF CPU32}
  4997. procedure TJclStackInfoList.DelayStoreStack;
  4998. var
  4999. StackPtr: PJclAddr;
  5000. StackDataSize: Cardinal;
  5001. begin
  5002. if Assigned(FStackData) then
  5003. begin
  5004. FreeMem(FStackData);
  5005. FStackData := nil;
  5006. end;
  5007. // We define the bottom of the valid stack to be the current ESP pointer
  5008. if BaseOfStack = 0 then
  5009. begin
  5010. BaseOfStack := TJclAddr(GetStackPointer);
  5011. FFramePointer := GetFramePointer;
  5012. end;
  5013. // Get a pointer to the current bottom of the stack
  5014. StackPtr := PJclAddr(BaseOfStack);
  5015. if TJclAddr(StackPtr) < TopOfStack then
  5016. begin
  5017. StackDataSize := TopOfStack - TJclAddr(StackPtr);
  5018. GetMem(FStackData, StackDataSize);
  5019. System.Move(StackPtr^, FStackData^, StackDataSize);
  5020. //CopyMemory(FStackData, StackPtr, StackDataSize);
  5021. end;
  5022. FStackOffset := Int64(FStackData) - Int64(StackPtr);
  5023. FFramePointer := Pointer(TJclAddr(FFramePointer) + FStackOffset);
  5024. TopOfStack := TopOfStack + FStackOffset;
  5025. end;
  5026. {$ENDIF CPU32}
  5027. // Validate that the code address is a valid code site
  5028. //
  5029. // Information from Intel Manual 24319102(2).pdf, Download the 6.5 MBs from:
  5030. // http://developer.intel.com/design/pentiumii/manuals/243191.htm
  5031. // Instruction format, Chapter 2 and The CALL instruction: page 3-53, 3-54
  5032. function TJclStackInfoList.ValidCallSite(CodeAddr: TJclAddr; out CallInstructionSize: Cardinal): Boolean;
  5033. var
  5034. CodeDWORD4: DWORD;
  5035. CodeDWORD8: DWORD;
  5036. C4P, C8P: PDWORD;
  5037. RM1, RM2, RM5: Byte;
  5038. begin
  5039. // todo: 64 bit version
  5040. // First check that the address is within range of our code segment!
  5041. Result := CodeAddr > 8;
  5042. if Result then
  5043. begin
  5044. C8P := PDWORD(CodeAddr - 8);
  5045. C4P := PDWORD(CodeAddr - 4);
  5046. Result := ValidCodeAddr(TJclAddr(C8P), FModuleInfoList) and not IsBadReadPtr(C8P, 8);
  5047. // Now check to see if the instruction preceding the return address
  5048. // could be a valid CALL instruction
  5049. if Result then
  5050. begin
  5051. try
  5052. CodeDWORD8 := PDWORD(C8P)^;
  5053. CodeDWORD4 := PDWORD(C4P)^;
  5054. // CodeDWORD8 = (ReturnAddr-5):(ReturnAddr-6):(ReturnAddr-7):(ReturnAddr-8)
  5055. // CodeDWORD4 = (ReturnAddr-1):(ReturnAddr-2):(ReturnAddr-3):(ReturnAddr-4)
  5056. // ModR/M bytes contain the following bits:
  5057. // Mod = (76)
  5058. // Reg/Opcode = (543)
  5059. // R/M = (210)
  5060. RM1 := (CodeDWORD4 shr 24) and $7;
  5061. RM2 := (CodeDWORD4 shr 16) and $7;
  5062. //RM3 := (CodeDWORD4 shr 8) and $7;
  5063. //RM4 := CodeDWORD4 and $7;
  5064. RM5 := (CodeDWORD8 shr 24) and $7;
  5065. //RM6 := (CodeDWORD8 shr 16) and $7;
  5066. //RM7 := (CodeDWORD8 shr 8) and $7;
  5067. // Check the instruction prior to the potential call site.
  5068. // We consider it a valid call site if we find a CALL instruction there
  5069. // Check the most common CALL variants first
  5070. if ((CodeDWORD8 and $FF000000) = $E8000000) then
  5071. // 5 bytes, "CALL NEAR REL32" (E8 cd)
  5072. CallInstructionSize := 5
  5073. else
  5074. if ((CodeDWORD4 and $F8FF0000) = $10FF0000) and not (RM1 in [4, 5]) then
  5075. // 2 bytes, "CALL NEAR [EAX]" (FF /2) where Reg = 010, Mod = 00, R/M <> 100 (1 extra byte)
  5076. // and R/M <> 101 (4 extra bytes)
  5077. CallInstructionSize := 2
  5078. else
  5079. if ((CodeDWORD4 and $F8FF0000) = $D0FF0000) then
  5080. // 2 bytes, "CALL NEAR EAX" (FF /2) where Reg = 010 and Mod = 11
  5081. CallInstructionSize := 2
  5082. else
  5083. if ((CodeDWORD4 and $00FFFF00) = $0014FF00) then
  5084. // 3 bytes, "CALL NEAR [EAX+EAX*i]" (FF /2) where Reg = 010, Mod = 00 and RM = 100
  5085. // SIB byte not validated
  5086. CallInstructionSize := 3
  5087. else
  5088. if ((CodeDWORD4 and $00F8FF00) = $0050FF00) and (RM2 <> 4) then
  5089. // 3 bytes, "CALL NEAR [EAX+$12]" (FF /2) where Reg = 010, Mod = 01 and RM <> 100 (1 extra byte)
  5090. CallInstructionSize := 3
  5091. else
  5092. if ((CodeDWORD4 and $0000FFFF) = $000054FF) then
  5093. // 4 bytes, "CALL NEAR [EAX+EAX+$12]" (FF /2) where Reg = 010, Mod = 01 and RM = 100
  5094. // SIB byte not validated
  5095. CallInstructionSize := 4
  5096. else
  5097. if ((CodeDWORD8 and $FFFF0000) = $15FF0000) then
  5098. // 6 bytes, "CALL NEAR [$12345678]" (FF /2) where Reg = 010, Mod = 00 and RM = 101
  5099. CallInstructionSize := 6
  5100. else
  5101. if ((CodeDWORD8 and $F8FF0000) = $90FF0000) and (RM5 <> 4) then
  5102. // 6 bytes, "CALL NEAR [EAX+$12345678]" (FF /2) where Reg = 010, Mod = 10 and RM <> 100 (1 extra byte)
  5103. CallInstructionSize := 6
  5104. else
  5105. if ((CodeDWORD8 and $00FFFF00) = $0094FF00) then
  5106. // 7 bytes, "CALL NEAR [EAX+EAX+$1234567]" (FF /2) where Reg = 010, Mod = 10 and RM = 100
  5107. CallInstructionSize := 7
  5108. else
  5109. if ((CodeDWORD8 and $0000FF00) = $00009A00) then
  5110. // 7 bytes, "CALL FAR $1234:12345678" (9A ptr16:32)
  5111. CallInstructionSize := 7
  5112. else
  5113. Result := False;
  5114. // Because we're not doing a complete disassembly, we will potentially report
  5115. // false positives. If there is odd code that uses the CALL 16:32 format, we
  5116. // can also get false negatives.
  5117. except
  5118. Result := False;
  5119. end;
  5120. end;
  5121. end;
  5122. end;
  5123. {$IFNDEF STACKFRAMES_ON}
  5124. {$STACKFRAMES OFF}
  5125. {$ENDIF ~STACKFRAMES_ON}
  5126. function TJclStackInfoList.ValidStackAddr(StackAddr: TJclAddr): Boolean;
  5127. begin
  5128. Result := (BaseOfStack < StackAddr) and (StackAddr < TopOfStack);
  5129. end;
  5130. //=== Exception frame info routines ==========================================
  5131. function JclCreateExceptFrameList(AIgnoreLevels: Integer): TJclExceptFrameList;
  5132. begin
  5133. Result := TJclExceptFrameList.Create(AIgnoreLevels);
  5134. GlobalStackList.AddObject(Result);
  5135. end;
  5136. function JclLastExceptFrameList: TJclExceptFrameList;
  5137. begin
  5138. Result := GlobalStackList.LastExceptFrameList[GetCurrentThreadID];
  5139. end;
  5140. function JclGetExceptFrameList(ThreadID: DWORD): TJclExceptFrameList;
  5141. begin
  5142. Result := GlobalStackList.LastExceptFrameList[ThreadID];
  5143. end;
  5144. procedure DoExceptFrameTrace;
  5145. begin
  5146. // Ignore first 2 levels; the First level is an undefined frame (I haven't a
  5147. // clue as to where it comes from. The second level is the try..finally block
  5148. // in DoExceptNotify.
  5149. JclCreateExceptFrameList(4);
  5150. end;
  5151. {$OVERFLOWCHECKS OFF}
  5152. function GetJmpDest(Jmp: PJmpInstruction): Pointer;
  5153. begin
  5154. // TODO : 64 bit version
  5155. if Jmp^.opCode = $E9 then
  5156. Result := Pointer(TJclAddr(Jmp) + TJclAddr(Jmp^.distance) + 5)
  5157. else
  5158. if Jmp.opCode = $EB then
  5159. Result := Pointer(TJclAddr(Jmp) + TJclAddr(ShortInt(Jmp^.distance)) + 2)
  5160. else
  5161. Result := nil;
  5162. if (Result <> nil) and (PJmpTable(Result).OPCode = $25FF) then
  5163. if not IsBadReadPtr(PJmpTable(Result).Ptr, SizeOf(Pointer)) then
  5164. Result := Pointer(PJclAddr(PJmpTable(Result).Ptr)^);
  5165. end;
  5166. {$IFDEF OVERFLOWCHECKS_ON}
  5167. {$OVERFLOWCHECKS ON}
  5168. {$ENDIF OVERFLOWCHECKS_ON}
  5169. //=== { TJclExceptFrame } ====================================================
  5170. constructor TJclExceptFrame.Create(AFrameLocation: Pointer; AExcDesc: PExcDesc);
  5171. begin
  5172. inherited Create;
  5173. FFrameKind := efkUnknown;
  5174. FFrameLocation := AFrameLocation;
  5175. FCodeLocation := nil;
  5176. AnalyseExceptFrame(AExcDesc);
  5177. end;
  5178. {$RANGECHECKS OFF}
  5179. procedure TJclExceptFrame.AnalyseExceptFrame(AExcDesc: PExcDesc);
  5180. var
  5181. Dest: Pointer;
  5182. LocInfo: TJclLocationInfo;
  5183. FixedProcedureName: string;
  5184. DotPos, I: Integer;
  5185. begin
  5186. Dest := GetJmpDest(@AExcDesc^.Jmp);
  5187. if Dest <> nil then
  5188. begin
  5189. // get frame kind
  5190. LocInfo := GetLocationInfo(Dest);
  5191. if CompareText(LocInfo.UnitName, 'system') = 0 then
  5192. begin
  5193. FixedProcedureName := LocInfo.ProcedureName;
  5194. DotPos := Pos('.', FixedProcedureName);
  5195. if DotPos > 0 then
  5196. FixedProcedureName := Copy(FixedProcedureName, DotPos + 1, Length(FixedProcedureName) - DotPos);
  5197. if CompareText(FixedProcedureName, '@HandleAnyException') = 0 then
  5198. FFrameKind := efkAnyException
  5199. else
  5200. if CompareText(FixedProcedureName, '@HandleOnException') = 0 then
  5201. FFrameKind := efkOnException
  5202. else
  5203. if CompareText(FixedProcedureName, '@HandleAutoException') = 0 then
  5204. FFrameKind := efkAutoException
  5205. else
  5206. if CompareText(FixedProcedureName, '@HandleFinally') = 0 then
  5207. FFrameKind := efkFinally;
  5208. end;
  5209. // get location
  5210. if FFrameKind <> efkUnknown then
  5211. begin
  5212. FCodeLocation := GetJmpDest(PJmpInstruction(TJclAddr(@AExcDesc^.Instructions)));
  5213. if FCodeLocation = nil then
  5214. FCodeLocation := @AExcDesc^.Instructions;
  5215. end
  5216. else
  5217. begin
  5218. FCodeLocation := GetJmpDest(PJmpInstruction(TJclAddr(AExcDesc)));
  5219. if FCodeLocation = nil then
  5220. FCodeLocation := AExcDesc;
  5221. end;
  5222. // get on handlers
  5223. if FFrameKind = efkOnException then
  5224. begin
  5225. SetLength(FExcTab, AExcDesc^.Cnt);
  5226. for I := 0 to AExcDesc^.Cnt - 1 do
  5227. begin
  5228. if AExcDesc^.ExcTab[I].VTable = nil then
  5229. begin
  5230. SetLength(FExcTab, I);
  5231. Break;
  5232. end
  5233. else
  5234. FExcTab[I] := AExcDesc^.ExcTab[I];
  5235. end;
  5236. end;
  5237. end;
  5238. end;
  5239. {$IFDEF RANGECHECKS_ON}
  5240. {$RANGECHECKS ON}
  5241. {$ENDIF RANGECHECKS_ON}
  5242. function TJclExceptFrame.Handles(ExceptObj: TObject): Boolean;
  5243. var
  5244. Handler: Pointer;
  5245. begin
  5246. Result := HandlerInfo(ExceptObj, Handler);
  5247. end;
  5248. {$OVERFLOWCHECKS OFF}
  5249. function TJclExceptFrame.HandlerInfo(ExceptObj: TObject; out HandlerAt: Pointer): Boolean;
  5250. var
  5251. I: Integer;
  5252. ObjVTable, VTable, ParentVTable: Pointer;
  5253. begin
  5254. Result := FrameKind in [efkAnyException, efkAutoException];
  5255. if not Result and (FrameKind = efkOnException) then
  5256. begin
  5257. HandlerAt := nil;
  5258. ObjVTable := Pointer(ExceptObj.ClassType);
  5259. for I := Low(FExcTab) to High(FExcTab) do
  5260. begin
  5261. VTable := ObjVTable;
  5262. Result := FExcTab[I].VTable = nil;
  5263. while (not Result) and (VTable <> nil) do
  5264. begin
  5265. Result := (FExcTab[I].VTable = VTable) or
  5266. (PShortString(PPointer(PJclAddr(FExcTab[I].VTable)^ + TJclAddr(vmtClassName))^)^ =
  5267. PShortString(PPointer(TJclAddr(VTable) + TJclAddr(vmtClassName))^)^);
  5268. if Result then
  5269. HandlerAt := FExcTab[I].Handler
  5270. else
  5271. begin
  5272. ParentVTable := TClass(VTable).ClassParent;
  5273. if ParentVTable = VTable then
  5274. VTable := nil
  5275. else
  5276. VTable := ParentVTable;
  5277. end;
  5278. end;
  5279. if Result then
  5280. Break;
  5281. end;
  5282. end
  5283. else
  5284. if Result then
  5285. HandlerAt := FCodeLocation
  5286. else
  5287. HandlerAt := nil;
  5288. end;
  5289. {$IFDEF OVERFLOWCHECKS_ON}
  5290. {$OVERFLOWCHECKS ON}
  5291. {$ENDIF OVERFLOWCHECKS_ON}
  5292. //=== { TJclExceptFrameList } ================================================
  5293. constructor TJclExceptFrameList.Create(AIgnoreLevels: Integer);
  5294. begin
  5295. inherited Create;
  5296. FIgnoreLevels := AIgnoreLevels;
  5297. TraceExceptionFrames;
  5298. end;
  5299. function TJclExceptFrameList.AddFrame(AFrame: PExcFrame): TJclExceptFrame;
  5300. begin
  5301. Result := TJclExceptFrame.Create(AFrame, AFrame^.Desc);
  5302. Add(Result);
  5303. end;
  5304. function TJclExceptFrameList.GetItems(Index: Integer): TJclExceptFrame;
  5305. begin
  5306. Result := TJclExceptFrame(Get(Index));
  5307. end;
  5308. procedure TJclExceptFrameList.TraceExceptionFrames;
  5309. {$IFDEF CPU32}
  5310. var
  5311. ExceptionPointer: PExcFrame;
  5312. Level: Integer;
  5313. ModulesList: TJclModuleInfoList;
  5314. begin
  5315. Clear;
  5316. ModulesList := GlobalModulesList.CreateModulesList;
  5317. try
  5318. Level := 0;
  5319. ExceptionPointer := GetExceptionPointer;
  5320. while TJclAddr(ExceptionPointer) <> High(TJclAddr) do
  5321. begin
  5322. if (Level >= IgnoreLevels) and ValidCodeAddr(TJclAddr(ExceptionPointer^.Desc), ModulesList) then
  5323. AddFrame(ExceptionPointer);
  5324. Inc(Level);
  5325. ExceptionPointer := ExceptionPointer^.next;
  5326. end;
  5327. finally
  5328. GlobalModulesList.FreeModulesList(ModulesList);
  5329. end;
  5330. end;
  5331. {$ENDIF CPU32}
  5332. {$IFDEF CPU64}
  5333. begin
  5334. // TODO: 64-bit version
  5335. end;
  5336. {$ENDIF CPU64}
  5337. //=== Exception hooking ======================================================
  5338. var
  5339. TrackingActiveCount: Integer;
  5340. IgnoredExceptions: TThreadList = nil;
  5341. IgnoredExceptionClassNames: TStringList = nil;
  5342. IgnoredExceptionClassNamesCritSect: TJclCriticalSection = nil;
  5343. procedure AddIgnoredException(const ExceptionClass: TClass);
  5344. begin
  5345. if Assigned(ExceptionClass) then
  5346. begin
  5347. if not Assigned(IgnoredExceptions) then
  5348. IgnoredExceptions := TThreadList.Create;
  5349. IgnoredExceptions.Add(ExceptionClass);
  5350. end;
  5351. end;
  5352. procedure AddIgnoredExceptionByName(const AExceptionClassName: string);
  5353. begin
  5354. if AExceptionClassName <> '' then
  5355. begin
  5356. if not Assigned(IgnoredExceptionClassNamesCritSect) then
  5357. IgnoredExceptionClassNamesCritSect := TJclCriticalSection.Create;
  5358. if not Assigned(IgnoredExceptionClassNames) then
  5359. begin
  5360. IgnoredExceptionClassNames := TStringList.Create;
  5361. IgnoredExceptionClassNames.Duplicates := dupIgnore;
  5362. IgnoredExceptionClassNames.Sorted := True;
  5363. end;
  5364. IgnoredExceptionClassNamesCritSect.Enter;
  5365. try
  5366. IgnoredExceptionClassNames.Add(AExceptionClassName);
  5367. finally
  5368. IgnoredExceptionClassNamesCritSect.Leave;
  5369. end;
  5370. end;
  5371. end;
  5372. procedure RemoveIgnoredException(const ExceptionClass: TClass);
  5373. var
  5374. ClassList: TList;
  5375. begin
  5376. if Assigned(ExceptionClass) and Assigned(IgnoredExceptions) then
  5377. begin
  5378. ClassList := IgnoredExceptions.LockList;
  5379. try
  5380. ClassList.Remove(ExceptionClass);
  5381. finally
  5382. IgnoredExceptions.UnlockList;
  5383. end;
  5384. end;
  5385. end;
  5386. procedure RemoveIgnoredExceptionByName(const AExceptionClassName: string);
  5387. var
  5388. Index: Integer;
  5389. begin
  5390. if Assigned(IgnoredExceptionClassNames) and (AExceptionClassName <> '') then
  5391. begin
  5392. IgnoredExceptionClassNamesCritSect.Enter;
  5393. try
  5394. Index := IgnoredExceptionClassNames.IndexOf(AExceptionClassName);
  5395. if Index <> -1 then
  5396. IgnoredExceptionClassNames.Delete(Index);
  5397. finally
  5398. IgnoredExceptionClassNamesCritSect.Leave;
  5399. end;
  5400. end;
  5401. end;
  5402. function IsIgnoredException(const ExceptionClass: TClass): Boolean;
  5403. var
  5404. ClassList: TList;
  5405. Index: Integer;
  5406. begin
  5407. Result := False;
  5408. if Assigned(IgnoredExceptions) and not (stTraceAllExceptions in JclStackTrackingOptions) then
  5409. begin
  5410. ClassList := IgnoredExceptions.LockList;
  5411. try
  5412. for Index := 0 to ClassList.Count - 1 do
  5413. if ExceptionClass.InheritsFrom(TClass(ClassList.Items[Index])) then
  5414. begin
  5415. Result := True;
  5416. Break;
  5417. end;
  5418. finally
  5419. IgnoredExceptions.UnlockList;
  5420. end;
  5421. end;
  5422. if not Result and Assigned(IgnoredExceptionClassNames) and not (stTraceAllExceptions in JclStackTrackingOptions) then
  5423. begin
  5424. IgnoredExceptionClassNamesCritSect.Enter;
  5425. try
  5426. Result := IgnoredExceptionClassNames.IndexOf(ExceptionClass.ClassName) <> -1;
  5427. if not Result then
  5428. for Index := 0 to IgnoredExceptionClassNames.Count - 1 do
  5429. if InheritsFromByName(ExceptionClass, IgnoredExceptionClassNames[Index]) then
  5430. begin
  5431. Result := True;
  5432. Break;
  5433. end;
  5434. finally
  5435. IgnoredExceptionClassNamesCritSect.Leave;
  5436. end;
  5437. end;
  5438. end;
  5439. procedure AddModule(const ModuleName: string);
  5440. begin
  5441. GlobalModulesList.AddModule(ModuleName);
  5442. end;
  5443. procedure DoExceptNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean;
  5444. BaseOfStack: Pointer);
  5445. begin
  5446. if (TrackingActiveCount > 0) and (not (stDisableIfDebuggerAttached in JclStackTrackingOptions) or (not IsDebuggerAttached)) and
  5447. Assigned(ExceptObj) and (not IsIgnoredException(ExceptObj.ClassType)) and
  5448. (not (stMainThreadOnly in JclStackTrackingOptions) or (GetCurrentThreadId = MainThreadID)) then
  5449. begin
  5450. if stStack in JclStackTrackingOptions then
  5451. DoExceptionStackTrace(ExceptObj, ExceptAddr, OSException, BaseOfStack);
  5452. if stExceptFrame in JclStackTrackingOptions then
  5453. DoExceptFrameTrace;
  5454. end;
  5455. end;
  5456. function JclStartExceptionTracking: Boolean;
  5457. begin
  5458. {Increment the tracking count only if exceptions are already being tracked or tracking can be started
  5459. successfully.}
  5460. if TrackingActiveCount = 0 then
  5461. begin
  5462. if JclHookExceptions and JclAddExceptNotifier(DoExceptNotify, npFirstChain) then
  5463. begin
  5464. TrackingActiveCount := 1;
  5465. Result := True;
  5466. end
  5467. else
  5468. Result := False;
  5469. end
  5470. else
  5471. begin
  5472. Inc(TrackingActiveCount);
  5473. Result := False;
  5474. end;
  5475. end;
  5476. function JclStopExceptionTracking: Boolean;
  5477. begin
  5478. {If the current tracking count is 1, an attempt is made to stop tracking exceptions. If successful the
  5479. tracking count is set back to 0. If the current tracking count is > 1 it is simply decremented.}
  5480. if TrackingActiveCount = 1 then
  5481. begin
  5482. Result := JclRemoveExceptNotifier(DoExceptNotify) and JclUnhookExceptions;
  5483. if Result then
  5484. Dec(TrackingActiveCount);
  5485. end
  5486. else
  5487. begin
  5488. if TrackingActiveCount > 0 then
  5489. Dec(TrackingActiveCount);
  5490. Result := False;
  5491. end;
  5492. end;
  5493. function JclExceptionTrackingActive: Boolean;
  5494. begin
  5495. Result := TrackingActiveCount > 0;
  5496. end;
  5497. function JclTrackExceptionsFromLibraries: Boolean;
  5498. begin
  5499. Result := TrackingActiveCount > 0;
  5500. if Result then
  5501. JclInitializeLibrariesHookExcept;
  5502. end;
  5503. //=== Thread exception tracking support ======================================
  5504. var
  5505. RegisteredThreadList: TJclDebugThreadList;
  5506. function JclDebugThreadList: TJclDebugThreadList;
  5507. begin
  5508. if RegisteredThreadList = nil then
  5509. RegisteredThreadList := TJclDebugThreadList.Create;
  5510. Result := RegisteredThreadList;
  5511. end;
  5512. type
  5513. TKernel32_CreateThread = function(SecurityAttributes: Pointer; StackSize: LongWord;
  5514. ThreadFunc: TThreadFunc; Parameter: Pointer;
  5515. CreationFlags: LongWord; var ThreadId: LongWord): Integer; stdcall;
  5516. TKernel32_ExitThread = procedure(ExitCode: Integer); stdcall;
  5517. var
  5518. ThreadsHooked: Boolean;
  5519. Kernel32_CreateThread: TKernel32_CreateThread = nil;
  5520. Kernel32_ExitThread: TKernel32_ExitThread = nil;
  5521. function HookedCreateThread(SecurityAttributes: Pointer; StackSize: LongWord;
  5522. ThreadFunc: TThreadFunc; Parameter: Pointer;
  5523. CreationFlags: LongWord; ThreadId: PLongWord): Integer; stdcall;
  5524. var
  5525. LocalThreadId: LongWord;
  5526. begin
  5527. Result := Kernel32_CreateThread(SecurityAttributes, StackSize, ThreadFunc, Parameter, CreationFlags, LocalThreadId);
  5528. if Result <> 0 then
  5529. begin
  5530. JclDebugThreadList.RegisterThreadID(LocalThreadId);
  5531. if ThreadId <> nil then
  5532. begin
  5533. ThreadId^ := LocalThreadId;
  5534. end;
  5535. end;
  5536. end;
  5537. procedure HookedExitThread(ExitCode: Integer); stdcall;
  5538. begin
  5539. JclDebugThreadList.UnregisterThreadID(GetCurrentThreadID);
  5540. Kernel32_ExitThread(ExitCode);
  5541. end;
  5542. function JclHookThreads: Boolean;
  5543. var
  5544. ProcAddrCache: Pointer;
  5545. begin
  5546. if not ThreadsHooked then
  5547. begin
  5548. ProcAddrCache := GetProcAddress(GetModuleHandle(kernel32), 'CreateThread');
  5549. with TJclPeMapImgHooks do
  5550. Result := ReplaceImport(SystemBase, kernel32, ProcAddrCache, @HookedCreateThread);
  5551. if Result then
  5552. begin
  5553. @Kernel32_CreateThread := ProcAddrCache;
  5554. ProcAddrCache := GetProcAddress(GetModuleHandle(kernel32), 'ExitThread');
  5555. with TJclPeMapImgHooks do
  5556. Result := ReplaceImport(SystemBase, kernel32, ProcAddrCache, @HookedExitThread);
  5557. if Result then
  5558. @Kernel32_ExitThread := ProcAddrCache
  5559. else
  5560. with TJclPeMapImgHooks do
  5561. ReplaceImport(SystemBase, kernel32, @HookedCreateThread, @Kernel32_CreateThread);
  5562. end;
  5563. ThreadsHooked := Result;
  5564. end
  5565. else
  5566. Result := True;
  5567. end;
  5568. function JclUnhookThreads: Boolean;
  5569. begin
  5570. if ThreadsHooked then
  5571. begin
  5572. with TJclPeMapImgHooks do
  5573. begin
  5574. ReplaceImport(SystemBase, kernel32, @HookedCreateThread, @Kernel32_CreateThread);
  5575. ReplaceImport(SystemBase, kernel32, @HookedExitThread, @Kernel32_ExitThread);
  5576. end;
  5577. Result := True;
  5578. ThreadsHooked := False;
  5579. end
  5580. else
  5581. Result := True;
  5582. end;
  5583. function JclThreadsHooked: Boolean;
  5584. begin
  5585. Result := ThreadsHooked;
  5586. end;
  5587. //=== { TJclDebugThread } ====================================================
  5588. constructor TJclDebugThread.Create(ASuspended: Boolean; const AThreadName: string);
  5589. begin
  5590. FThreadName := AThreadName;
  5591. inherited Create(True);
  5592. JclDebugThreadList.RegisterThread(Self, AThreadName);
  5593. if not ASuspended then
  5594. {$IFDEF RTL210_UP}
  5595. Suspended := False;
  5596. {$ELSE ~RTL210_UP}
  5597. Resume;
  5598. {$ENDIF ~RTL210_UP}
  5599. end;
  5600. destructor TJclDebugThread.Destroy;
  5601. begin
  5602. JclDebugThreadList.UnregisterThread(Self);
  5603. inherited Destroy;
  5604. end;
  5605. procedure TJclDebugThread.DoHandleException;
  5606. begin
  5607. GlobalStackList.LockThreadID(ThreadID);
  5608. try
  5609. DoSyncHandleException;
  5610. finally
  5611. GlobalStackList.UnlockThreadID;
  5612. end;
  5613. end;
  5614. procedure TJclDebugThread.DoNotify;
  5615. begin
  5616. JclDebugThreadList.DoSyncException(Self);
  5617. end;
  5618. procedure TJclDebugThread.DoSyncHandleException;
  5619. begin
  5620. // Note: JclLastExceptStackList and JclLastExceptFrameList returns information
  5621. // for this Thread ID instead of MainThread ID here to allow use a common
  5622. // exception handling routine easily.
  5623. // Any other call of those JclLastXXX routines from another thread at the same
  5624. // time will return expected information for current Thread ID.
  5625. DoNotify;
  5626. end;
  5627. function TJclDebugThread.GetThreadInfo: string;
  5628. begin
  5629. Result := JclDebugThreadList.ThreadInfos[ThreadID];
  5630. end;
  5631. procedure TJclDebugThread.HandleException(Sender: TObject);
  5632. begin
  5633. FSyncException := Sender;
  5634. try
  5635. if not Assigned(FSyncException) then
  5636. FSyncException := Exception(ExceptObject);
  5637. if Assigned(FSyncException) and not IsIgnoredException(FSyncException.ClassType) then
  5638. Synchronize(DoHandleException);
  5639. finally
  5640. FSyncException := nil;
  5641. end;
  5642. end;
  5643. //=== { TJclDebugThreadList } ================================================
  5644. type
  5645. TThreadAccess = class(TThread);
  5646. constructor TJclDebugThreadList.Create;
  5647. begin
  5648. FLock := TJclCriticalSection.Create;
  5649. FReadLock := TJclCriticalSection.Create;
  5650. FList := TObjectList.Create;
  5651. FSaveCreationStack := False;
  5652. end;
  5653. destructor TJclDebugThreadList.Destroy;
  5654. begin
  5655. FreeAndNil(FList);
  5656. FreeAndNil(FLock);
  5657. FreeAndNil(FReadLock);
  5658. inherited Destroy;
  5659. end;
  5660. function TJclDebugThreadList.AddStackListToLocationInfoList(ThreadID: DWORD; AList: TJclLocationInfoList): Boolean;
  5661. var
  5662. I: Integer;
  5663. List: TJclStackInfoList;
  5664. begin
  5665. Result := False;
  5666. FReadLock.Enter;
  5667. try
  5668. I := IndexOfThreadID(ThreadID);
  5669. if (I <> -1) and Assigned(TJclDebugThreadInfo(FList[I]).StackList) then
  5670. begin
  5671. List := TJclDebugThreadInfo(FList[I]).StackList;
  5672. AList.AddStackInfoList(List);
  5673. Result := True;
  5674. end;
  5675. finally
  5676. FReadLock.Leave;
  5677. end;
  5678. end;
  5679. procedure TJclDebugThreadList.DoSyncException(Thread: TJclDebugThread);
  5680. begin
  5681. if Assigned(FOnSyncException) then
  5682. FOnSyncException(Thread);
  5683. end;
  5684. procedure TJclDebugThreadList.DoSyncThreadRegistered;
  5685. begin
  5686. if Assigned(FOnThreadRegistered) then
  5687. FOnThreadRegistered(FRegSyncThreadID);
  5688. end;
  5689. procedure TJclDebugThreadList.DoSyncThreadUnregistered;
  5690. begin
  5691. if Assigned(FOnThreadUnregistered) then
  5692. FOnThreadUnregistered(FUnregSyncThreadID);
  5693. end;
  5694. procedure TJclDebugThreadList.DoThreadRegistered(Thread: TThread);
  5695. begin
  5696. if Assigned(FOnThreadRegistered) then
  5697. begin
  5698. FRegSyncThreadID := Thread.ThreadID;
  5699. TThreadAccess(Thread).Synchronize(DoSyncThreadRegistered);
  5700. end;
  5701. end;
  5702. procedure TJclDebugThreadList.DoThreadUnregistered(Thread: TThread);
  5703. begin
  5704. if Assigned(FOnThreadUnregistered) then
  5705. begin
  5706. FUnregSyncThreadID := Thread.ThreadID;
  5707. TThreadAccess(Thread).Synchronize(DoSyncThreadUnregistered);
  5708. end;
  5709. end;
  5710. function TJclDebugThreadList.GetThreadClassNames(ThreadID: DWORD): string;
  5711. begin
  5712. Result := GetThreadValues(ThreadID, 1);
  5713. end;
  5714. function TJclDebugThreadList.GetThreadCreationTime(ThreadID: DWORD): TDateTime;
  5715. var
  5716. I: Integer;
  5717. begin
  5718. FReadLock.Enter;
  5719. try
  5720. I := IndexOfThreadID(ThreadID);
  5721. if I <> -1 then
  5722. Result := TJclDebugThreadInfo(FList[I]).CreationTime
  5723. else
  5724. Result := 0;
  5725. finally
  5726. FReadLock.Leave;
  5727. end;
  5728. end;
  5729. function TJclDebugThreadList.GetThreadIDCount: Integer;
  5730. begin
  5731. FReadLock.Enter;
  5732. try
  5733. Result := FList.Count;
  5734. finally
  5735. FReadLock.Leave;
  5736. end;
  5737. end;
  5738. function TJclDebugThreadList.GetThreadHandle(Index: Integer): THandle;
  5739. begin
  5740. FReadLock.Enter;
  5741. try
  5742. Result := TJclDebugThreadInfo(FList[Index]).ThreadHandle;
  5743. finally
  5744. FReadLock.Leave;
  5745. end;
  5746. end;
  5747. function TJclDebugThreadList.GetThreadID(Index: Integer): DWORD;
  5748. begin
  5749. FReadLock.Enter;
  5750. try
  5751. Result := TJclDebugThreadInfo(FList[Index]).ThreadID;
  5752. finally
  5753. FReadLock.Leave;
  5754. end;
  5755. end;
  5756. function TJclDebugThreadList.GetThreadInfos(ThreadID: DWORD): string;
  5757. begin
  5758. Result := GetThreadValues(ThreadID, 2);
  5759. end;
  5760. function TJclDebugThreadList.GetThreadNames(ThreadID: DWORD): string;
  5761. begin
  5762. Result := GetThreadValues(ThreadID, 0);
  5763. end;
  5764. function TJclDebugThreadList.GetThreadParentID(ThreadID: DWORD): DWORD;
  5765. var
  5766. I: Integer;
  5767. begin
  5768. FReadLock.Enter;
  5769. try
  5770. I := IndexOfThreadID(ThreadID);
  5771. if I <> -1 then
  5772. Result := TJclDebugThreadInfo(FList[I]).ParentThreadID
  5773. else
  5774. Result := 0;
  5775. finally
  5776. FReadLock.Leave;
  5777. end;
  5778. end;
  5779. function TJclDebugThreadList.GetThreadValues(ThreadID: DWORD; Index: Integer): string;
  5780. var
  5781. I: Integer;
  5782. begin
  5783. FReadLock.Enter;
  5784. try
  5785. I := IndexOfThreadID(ThreadID);
  5786. if I <> -1 then
  5787. begin
  5788. case Index of
  5789. 0:
  5790. Result := TJclDebugThreadInfo(FList[I]).ThreadName;
  5791. 1:
  5792. Result := TJclDebugThreadInfo(FList[I]).ThreadClassName;
  5793. 2:
  5794. Result := Format('%.8x [%s] "%s"', [ThreadID, TJclDebugThreadInfo(FList[I]).ThreadClassName,
  5795. TJclDebugThreadInfo(FList[I]).ThreadName]);
  5796. end;
  5797. end
  5798. else
  5799. Result := '';
  5800. finally
  5801. FReadLock.Leave;
  5802. end;
  5803. end;
  5804. function TJclDebugThreadList.IndexOfThreadID(ThreadID: DWORD): Integer;
  5805. var
  5806. I: Integer;
  5807. begin
  5808. Result := -1;
  5809. for I := FList.Count - 1 downto 0 do
  5810. if TJclDebugThreadInfo(FList[I]).ThreadID = ThreadID then
  5811. begin
  5812. Result := I;
  5813. Break;
  5814. end;
  5815. end;
  5816. procedure TJclDebugThreadList.InternalRegisterThread(Thread: TThread; ThreadID: DWORD; const ThreadName: string);
  5817. var
  5818. I: Integer;
  5819. ThreadInfo: TJclDebugThreadInfo;
  5820. begin
  5821. FLock.Enter;
  5822. try
  5823. I := IndexOfThreadID(ThreadID);
  5824. if I = -1 then
  5825. begin
  5826. FReadLock.Enter;
  5827. try
  5828. FList.Add(TJclDebugThreadInfo.Create(GetCurrentThreadId, ThreadID, FSaveCreationStack));
  5829. ThreadInfo := TJclDebugThreadInfo(FList.Last);
  5830. if Assigned(Thread) then
  5831. begin
  5832. ThreadInfo.ThreadHandle := Thread.Handle;
  5833. ThreadInfo.ThreadClassName := Thread.ClassName;
  5834. end
  5835. else
  5836. begin
  5837. ThreadInfo.ThreadHandle := 0;
  5838. ThreadInfo.ThreadClassName := '';
  5839. end;
  5840. ThreadInfo.ThreadName := ThreadName;
  5841. finally
  5842. FReadLock.Leave;
  5843. end;
  5844. if Assigned(Thread) then
  5845. DoThreadRegistered(Thread);
  5846. end;
  5847. finally
  5848. FLock.Leave;
  5849. end;
  5850. end;
  5851. procedure TJclDebugThreadList.InternalUnregisterThread(Thread: TThread; ThreadID: DWORD);
  5852. var
  5853. I: Integer;
  5854. begin
  5855. FLock.Enter;
  5856. try
  5857. I := IndexOfThreadID(ThreadID);
  5858. if I <> -1 then
  5859. begin
  5860. if Assigned(Thread) then
  5861. DoThreadUnregistered(Thread);
  5862. FReadLock.Enter;
  5863. try
  5864. FList.Delete(I);
  5865. finally
  5866. FReadLock.Leave;
  5867. end;
  5868. end;
  5869. finally
  5870. FLock.Leave;
  5871. end;
  5872. end;
  5873. procedure TJclDebugThreadList.RegisterThread(Thread: TThread; const ThreadName: string);
  5874. begin
  5875. InternalRegisterThread(Thread, Thread.ThreadID, ThreadName);
  5876. end;
  5877. procedure TJclDebugThreadList.RegisterThreadID(AThreadID: DWORD);
  5878. begin
  5879. InternalRegisterThread(nil, AThreadID, '');
  5880. end;
  5881. procedure TJclDebugThreadList.UnregisterThread(Thread: TThread);
  5882. begin
  5883. InternalUnregisterThread(Thread, Thread.ThreadID);
  5884. end;
  5885. procedure TJclDebugThreadList.UnregisterThreadID(AThreadID: DWORD);
  5886. begin
  5887. InternalUnregisterThread(nil, AThreadID);
  5888. end;
  5889. //=== { TJclDebugThreadInfo } ================================================
  5890. constructor TJclDebugThreadInfo.Create(AParentThreadID, AThreadID: DWORD; AStack: Boolean);
  5891. begin
  5892. FCreationTime := Now;
  5893. FParentThreadID := AParentThreadID;
  5894. try
  5895. { TODO -oUSc : ... }
  5896. // FStackList := JclCreateStackList(True, 0, nil, True);//probably IgnoreLevels = 11
  5897. if AStack then
  5898. FStackList := TJclStackInfoList.Create(True, 0, nil, True, nil, nil)
  5899. else
  5900. FStackList := nil;
  5901. except
  5902. FStackList := nil;
  5903. end;
  5904. FThreadID := AThreadID;
  5905. end;
  5906. destructor TJclDebugThreadInfo.Destroy;
  5907. begin
  5908. FStackList.Free;
  5909. inherited Destroy;
  5910. end;
  5911. //=== { TJclCustomThreadInfo } ===============================================
  5912. constructor TJclCustomThreadInfo.Create;
  5913. var
  5914. StackClass: TJclCustomLocationInfoListClass;
  5915. begin
  5916. inherited Create;
  5917. StackClass := GetStackClass;
  5918. FCreationTime := 0;
  5919. FCreationStack := StackClass.Create;
  5920. FName := '';
  5921. FParentThreadID := 0;
  5922. FStack := StackClass.Create;
  5923. FThreadID := 0;
  5924. FValues := [];
  5925. end;
  5926. destructor TJclCustomThreadInfo.Destroy;
  5927. begin
  5928. FCreationStack.Free;
  5929. FStack.Free;
  5930. inherited Destroy;
  5931. end;
  5932. procedure TJclCustomThreadInfo.AssignTo(Dest: TPersistent);
  5933. begin
  5934. if Dest is TJclCustomThreadInfo then
  5935. begin
  5936. TJclCustomThreadInfo(Dest).FCreationTime := FCreationTime;
  5937. TJclCustomThreadInfo(Dest).FCreationStack.Assign(FCreationStack);
  5938. TJclCustomThreadInfo(Dest).FName := FName;
  5939. TJclCustomThreadInfo(Dest).FParentThreadID := FParentThreadID;
  5940. TJclCustomThreadInfo(Dest).FStack.Assign(FStack);
  5941. TJclCustomThreadInfo(Dest).FThreadID := FThreadID;
  5942. TJclCustomThreadInfo(Dest).FValues := FValues;
  5943. end
  5944. else
  5945. inherited AssignTo(Dest);
  5946. end;
  5947. function TJclCustomThreadInfo.GetStackClass: TJclCustomLocationInfoListClass;
  5948. begin
  5949. Result := TJclLocationInfoList;
  5950. end;
  5951. //=== { TJclThreadInfo } =====================================================
  5952. procedure TJclThreadInfo.Fill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions);
  5953. begin
  5954. InternalFill(AThreadHandle, AThreadID, AGatherOptions, False);
  5955. end;
  5956. procedure TJclThreadInfo.FillFromExceptThread(AGatherOptions: TJclThreadInfoOptions);
  5957. begin
  5958. InternalFill(0, GetCurrentThreadID, AGatherOptions, True);
  5959. end;
  5960. function TJclThreadInfo.GetAsString: string;
  5961. var
  5962. ExceptInfo, ThreadName, ThreadInfoStr: string;
  5963. begin
  5964. if tioIsMainThread in Values then
  5965. ThreadName := ' [MainThread]'
  5966. else
  5967. if tioName in Values then
  5968. ThreadName := Name
  5969. else
  5970. ThreadName := '';
  5971. ThreadInfoStr := '';
  5972. if tioCreationTime in Values then
  5973. ThreadInfoStr := ThreadInfoStr + Format(' CreationTime: %s', [DateTimeToStr(CreationTime)]);
  5974. if tioParentThreadID in Values then
  5975. ThreadInfoStr := ThreadInfoStr + Format(' ParentThreadID: %d', [ParentThreadID]);
  5976. ExceptInfo := Format('ThreadID: %d%s%s', [ThreadID, ThreadName, ThreadInfoStr]) + #13#10;
  5977. if tioStack in Values then
  5978. ExceptInfo := ExceptInfo + Stack.AsString;
  5979. if tioCreationStack in Values then
  5980. ExceptInfo := ExceptInfo + 'Created at:' + #13#10 + CreationStack.AsString + #13#10;
  5981. Result := ExceptInfo + #13#10;
  5982. end;
  5983. function TJclThreadInfo.GetStack(const AIndex: Integer): TJclLocationInfoList;
  5984. begin
  5985. case AIndex of
  5986. 1: Result := TJclLocationInfoList(FCreationStack);
  5987. 2: Result := TJclLocationInfoList(FStack);
  5988. else
  5989. Result := nil;
  5990. end;
  5991. end;
  5992. function TJclThreadInfo.GetStackClass: TJclCustomLocationInfoListClass;
  5993. begin
  5994. Result := TJclLocationInfoList;
  5995. end;
  5996. procedure TJclThreadInfo.InternalFill(AThreadHandle: THandle; AThreadID: DWORD; AGatherOptions: TJclThreadInfoOptions; AExceptThread: Boolean);
  5997. var
  5998. Idx: Integer;
  5999. List: TJclStackInfoList;
  6000. begin
  6001. if tioStack in AGatherOptions then
  6002. begin
  6003. if AExceptThread then
  6004. List := JclLastExceptStackList
  6005. else
  6006. List := JclCreateThreadStackTrace(True, AThreadHandle);
  6007. try
  6008. Stack.AddStackInfoList(List);
  6009. Values := Values + [tioStack];
  6010. except
  6011. { TODO -oUSc : ... }
  6012. end;
  6013. end;
  6014. ThreadID := AThreadID;
  6015. if tioIsMainThread in AGatherOptions then
  6016. begin
  6017. if MainThreadID = AThreadID then
  6018. Values := Values + [tioIsMainThread];
  6019. end;
  6020. if AGatherOptions * [tioName, tioCreationTime, tioParentThreadID, tioCreationStack] <> [] then
  6021. Idx := JclDebugThreadList.IndexOfThreadID(AThreadID)
  6022. else
  6023. Idx := -1;
  6024. if (tioName in AGatherOptions) and (Idx <> -1) then
  6025. begin
  6026. Name := JclDebugThreadList.ThreadNames[AThreadID];
  6027. Values := Values + [tioName];
  6028. end;
  6029. if (tioCreationTime in AGatherOptions) and (Idx <> -1) then
  6030. begin
  6031. CreationTime := JclDebugThreadList.ThreadCreationTime[AThreadID];
  6032. Values := Values + [tioCreationTime];
  6033. end;
  6034. if (tioParentThreadID in AGatherOptions) and (Idx <> -1) then
  6035. begin
  6036. ParentThreadID := JclDebugThreadList.ThreadParentIDs[AThreadID];
  6037. Values := Values + [tioParentThreadID];
  6038. end;
  6039. if (tioCreationStack in AGatherOptions) and (Idx <> -1) then
  6040. begin
  6041. try
  6042. if JclDebugThreadList.AddStackListToLocationInfoList(AThreadID, CreationStack) then
  6043. Values := Values + [tioCreationStack];
  6044. except
  6045. { TODO -oUSc : ... }
  6046. end;
  6047. end;
  6048. end;
  6049. //=== { TJclThreadInfoList } =================================================
  6050. constructor TJclThreadInfoList.Create;
  6051. begin
  6052. inherited Create;
  6053. FItems := TObjectList.Create;
  6054. FGatherOptions := [tioIsMainThread, tioName, tioCreationTime, tioParentThreadID, tioStack, tioCreationStack];
  6055. end;
  6056. destructor TJclThreadInfoList.Destroy;
  6057. begin
  6058. FItems.Free;
  6059. inherited Destroy;
  6060. end;
  6061. function TJclThreadInfoList.Add: TJclThreadInfo;
  6062. begin
  6063. FItems.Add(TJclThreadInfo.Create);
  6064. Result := TJclThreadInfo(FItems.Last);
  6065. end;
  6066. procedure TJclThreadInfoList.AssignTo(Dest: TPersistent);
  6067. var
  6068. I: Integer;
  6069. begin
  6070. if Dest is TJclThreadInfoList then
  6071. begin
  6072. TJclThreadInfoList(Dest).Clear;
  6073. for I := 0 to Count - 1 do
  6074. TJclThreadInfoList(Dest).Add.Assign(Items[I]);
  6075. TJclThreadInfoList(Dest).GatherOptions := FGatherOptions;
  6076. end
  6077. else
  6078. inherited AssignTo(Dest);
  6079. end;
  6080. procedure TJclThreadInfoList.Clear;
  6081. begin
  6082. FItems.Clear;
  6083. end;
  6084. function TJclThreadInfoList.GetAsString: string;
  6085. var
  6086. I: Integer;
  6087. begin
  6088. Result := '';
  6089. for I := 0 to Count - 1 do
  6090. Result := Result + Items[I].AsString + #13#10;
  6091. end;
  6092. procedure TJclThreadInfoList.Gather(AExceptThreadID: DWORD);
  6093. begin
  6094. InternalGather([], [AExceptThreadID]);
  6095. end;
  6096. procedure TJclThreadInfoList.GatherExclude(AThreadIDs: array of DWORD);
  6097. begin
  6098. InternalGather([], AThreadIDs);
  6099. end;
  6100. procedure TJclThreadInfoList.GatherInclude(AThreadIDs: array of DWORD);
  6101. begin
  6102. InternalGather(AThreadIDs, []);
  6103. end;
  6104. function TJclThreadInfoList.GetCount: Integer;
  6105. begin
  6106. Result := FItems.Count;
  6107. end;
  6108. function TJclThreadInfoList.GetItems(AIndex: Integer): TJclThreadInfo;
  6109. begin
  6110. Result := TJclThreadInfo(FItems[AIndex]);
  6111. end;
  6112. procedure TJclThreadInfoList.InternalGather(AIncludeThreadIDs, AExcludeThreadIDs: array of DWORD);
  6113. function OpenThread(ThreadID: DWORD): THandle;
  6114. type
  6115. TOpenThreadFunc = function(DesiredAccess: DWORD; InheritHandle: BOOL; ThreadID: DWORD): THandle; stdcall;
  6116. const
  6117. THREAD_SUSPEND_RESUME = $0002;
  6118. THREAD_GET_CONTEXT = $0008;
  6119. THREAD_QUERY_INFORMATION = $0040;
  6120. var
  6121. Kernel32Lib: THandle;
  6122. OpenThreadFunc: TOpenThreadFunc;
  6123. begin
  6124. Result := 0;
  6125. Kernel32Lib := GetModuleHandle(kernel32);
  6126. if Kernel32Lib <> 0 then
  6127. begin
  6128. // OpenThread only exists since Windows ME
  6129. OpenThreadFunc := GetProcAddress(Kernel32Lib, 'OpenThread');
  6130. if Assigned(OpenThreadFunc) then
  6131. Result := OpenThreadFunc(THREAD_SUSPEND_RESUME or THREAD_GET_CONTEXT or THREAD_QUERY_INFORMATION, False, ThreadID);
  6132. end;
  6133. end;
  6134. function SearchThreadInArray(AThreadIDs: array of DWORD; AThreadID: DWORD): Boolean;
  6135. var
  6136. I: Integer;
  6137. begin
  6138. Result := False;
  6139. if Length(AThreadIDs) > 0 then
  6140. for I := Low(AThreadIDs) to High(AThreadIDs) do
  6141. if AThreadIDs[I] = AThreadID then
  6142. begin
  6143. Result := True;
  6144. Break;
  6145. end;
  6146. end;
  6147. var
  6148. SnapProcHandle: THandle;
  6149. ThreadEntry: TThreadEntry32;
  6150. NextThread: Boolean;
  6151. ThreadIDList, ThreadHandleList: TList;
  6152. I: Integer;
  6153. PID, TID: DWORD;
  6154. ThreadHandle: THandle;
  6155. ThreadInfo: TJclThreadInfo;
  6156. begin
  6157. ThreadIDList := TList.Create;
  6158. ThreadHandleList := TList.Create;
  6159. try
  6160. SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  6161. if SnapProcHandle <> INVALID_HANDLE_VALUE then
  6162. try
  6163. PID := GetCurrentProcessId;
  6164. ThreadEntry.dwSize := SizeOf(ThreadEntry);
  6165. NextThread := Thread32First(SnapProcHandle, ThreadEntry);
  6166. while NextThread do
  6167. begin
  6168. if ThreadEntry.th32OwnerProcessID = PID then
  6169. begin
  6170. if SearchThreadInArray(AIncludeThreadIDs, ThreadEntry.th32ThreadID) or
  6171. not SearchThreadInArray(AExcludeThreadIDs, ThreadEntry.th32ThreadID) then
  6172. ThreadIDList.Add(Pointer(ThreadEntry.th32ThreadID));
  6173. end;
  6174. NextThread := Thread32Next(SnapProcHandle, ThreadEntry);
  6175. end;
  6176. finally
  6177. CloseHandle(SnapProcHandle);
  6178. end;
  6179. for I := 0 to ThreadIDList.Count - 1 do
  6180. begin
  6181. ThreadHandle := OpenThread(TJclAddr(ThreadIDList[I]));
  6182. ThreadHandleList.Add(Pointer(ThreadHandle));
  6183. if ThreadHandle <> 0 then
  6184. SuspendThread(ThreadHandle);
  6185. end;
  6186. try
  6187. for I := 0 to ThreadIDList.Count - 1 do
  6188. begin
  6189. ThreadHandle := THandle(ThreadHandleList[I]);
  6190. TID := TJclAddr(ThreadIDList[I]);
  6191. ThreadInfo := Add;
  6192. ThreadInfo.Fill(ThreadHandle, TID, FGatherOptions);
  6193. end;
  6194. finally
  6195. for I := 0 to ThreadHandleList.Count - 1 do
  6196. if ThreadHandleList[I] <> nil then
  6197. begin
  6198. ThreadHandle := THandle(ThreadHandleList[I]);
  6199. ResumeThread(ThreadHandle);
  6200. CloseHandle(ThreadHandle);
  6201. end;
  6202. end;
  6203. finally
  6204. ThreadIDList.Free;
  6205. ThreadHandleList.Free;
  6206. end;
  6207. end;
  6208. //== Miscellanuous ===========================================================
  6209. {$IFDEF MSWINDOWS}
  6210. {$IFNDEF WINSCP}
  6211. function EnableCrashOnCtrlScroll(const Enable: Boolean): Boolean;
  6212. const
  6213. CrashCtrlScrollKey = 'SYSTEM\CurrentControlSet\Services\i8042prt\Parameters';
  6214. CrashCtrlScrollName = 'CrashOnCtrlScroll';
  6215. var
  6216. Enabled: Integer;
  6217. begin
  6218. Enabled := 0;
  6219. if Enable then
  6220. Enabled := 1;
  6221. RegWriteInteger(HKEY_LOCAL_MACHINE, CrashCtrlScrollKey, CrashCtrlScrollName, Enabled);
  6222. Result := RegReadInteger(HKEY_LOCAL_MACHINE, CrashCtrlScrollKey, CrashCtrlScrollName) = Enabled;
  6223. end;
  6224. {$ENDIF ~WINSCP}
  6225. function IsDebuggerAttached: Boolean;
  6226. var
  6227. IsDebuggerPresent: function: Boolean; stdcall;
  6228. KernelHandle: THandle;
  6229. P: Pointer;
  6230. begin
  6231. KernelHandle := GetModuleHandle(kernel32);
  6232. @IsDebuggerPresent := GetProcAddress(KernelHandle, 'IsDebuggerPresent');
  6233. if @IsDebuggerPresent <> nil then
  6234. begin
  6235. // Win98+ / NT4+
  6236. Result := IsDebuggerPresent
  6237. end
  6238. else
  6239. begin
  6240. // Win9x uses thunk pointer outside the module when under a debugger
  6241. P := GetProcAddress(KernelHandle, 'GetProcAddress');
  6242. Result := TJclAddr(P) < KernelHandle;
  6243. end;
  6244. end;
  6245. function IsHandleValid(Handle: THandle): Boolean;
  6246. var
  6247. Duplicate: THandle;
  6248. Flags: DWORD;
  6249. begin
  6250. if IsWinNT then
  6251. begin
  6252. Flags := 0;
  6253. Result := GetHandleInformation(Handle, Flags);
  6254. end
  6255. else
  6256. Result := False;
  6257. if not Result then
  6258. begin
  6259. // DuplicateHandle is used as an additional check for those object types not
  6260. // supported by GetHandleInformation (e.g. according to the documentation,
  6261. // GetHandleInformation doesn't support window stations and desktop although
  6262. // tests show that it does). GetHandleInformation is tried first because its
  6263. // much faster. Additionally GetHandleInformation is only supported on NT...
  6264. Result := DuplicateHandle(GetCurrentProcess, Handle, GetCurrentProcess,
  6265. @Duplicate, 0, False, DUPLICATE_SAME_ACCESS);
  6266. if Result then
  6267. Result := CloseHandle(Duplicate);
  6268. end;
  6269. end;
  6270. {$ENDIF MSWINDOWS}
  6271. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  6272. function GetExceptionStackInfo(P: PExceptionRecord): Pointer;
  6273. const
  6274. cDelphiException = $0EEDFADE;
  6275. var
  6276. Stack: TJclStackInfoList;
  6277. Str: TStringList;
  6278. Trace: String;
  6279. Sz: Integer;
  6280. begin
  6281. if P^.ExceptionCode = cDelphiException then
  6282. Stack := JclCreateStackList(False, 3, P^.ExceptAddr)
  6283. else
  6284. Stack := JclCreateStackList(False, 3, P^.ExceptionAddress);
  6285. try
  6286. Str := TStringList.Create;
  6287. try
  6288. Stack.AddToStrings(Str, True, True, True, True);
  6289. Trace := Str.Text;
  6290. finally
  6291. FreeAndNil(Str);
  6292. end;
  6293. finally
  6294. FreeAndNil(Stack);
  6295. end;
  6296. if Trace <> '' then
  6297. begin
  6298. Sz := (Length(Trace) + 1) * SizeOf(Char);
  6299. GetMem(Result, Sz);
  6300. Move(Pointer(Trace)^, Result^, Sz);
  6301. end
  6302. else
  6303. Result := nil;
  6304. end;
  6305. function GetStackInfoString(Info: Pointer): string;
  6306. begin
  6307. Result := PChar(Info);
  6308. end;
  6309. procedure CleanUpStackInfo(Info: Pointer);
  6310. begin
  6311. FreeMem(Info);
  6312. end;
  6313. procedure SetupExceptionProcs;
  6314. begin
  6315. if not Assigned(Exception.GetExceptionStackInfoProc) then
  6316. begin
  6317. Exception.GetExceptionStackInfoProc := GetExceptionStackInfo;
  6318. Exception.GetStackInfoStringProc := GetStackInfoString;
  6319. Exception.CleanUpStackInfoProc := CleanUpStackInfo;
  6320. end;
  6321. end;
  6322. procedure ResetExceptionProcs;
  6323. begin
  6324. if @Exception.GetExceptionStackInfoProc = @GetExceptionStackInfo then
  6325. begin
  6326. Exception.GetExceptionStackInfoProc := nil;
  6327. Exception.GetStackInfoStringProc := nil;
  6328. Exception.CleanUpStackInfoProc := nil;
  6329. end;
  6330. end;
  6331. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  6332. initialization
  6333. DebugInfoCritSect := TJclCriticalSection.Create;
  6334. GlobalModulesList := TJclGlobalModulesList.Create;
  6335. GlobalStackList := TJclGlobalStackList.Create;
  6336. AddIgnoredException(EAbort);
  6337. {$IFDEF UNITVERSIONING}
  6338. RegisterUnitVersion(HInstance, UnitVersioning);
  6339. {$ENDIF UNITVERSIONING}
  6340. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  6341. SetupExceptionProcs;
  6342. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  6343. finalization
  6344. {$IFDEF HAS_EXCEPTION_STACKTRACE}
  6345. ResetExceptionProcs;
  6346. {$ENDIF HAS_EXCEPTION_STACKTRACE}
  6347. {$IFDEF UNITVERSIONING}
  6348. UnregisterUnitVersion(HInstance);
  6349. {$ENDIF UNITVERSIONING}
  6350. { TODO -oPV -cInvestigate : Calling JclStopExceptionTracking causes linking of various classes to
  6351. the code without a real need. Although there doesn't seem to be a way to unhook exceptions
  6352. safely because we need to be covered by JclHookExcept.Notifiers critical section }
  6353. JclStopExceptionTracking;
  6354. FreeAndNil(RegisteredThreadList);
  6355. FreeAndNil(DebugInfoList);
  6356. FreeAndNil(GlobalStackList);
  6357. FreeAndNil(GlobalModulesList);
  6358. FreeAndNil(DebugInfoCritSect);
  6359. FreeAndNil(InfoSourceClassList);
  6360. FreeAndNil(IgnoredExceptions);
  6361. FreeAndNil(IgnoredExceptionClassNames);
  6362. FreeAndNil(IgnoredExceptionClassNamesCritSect);
  6363. TJclDebugInfoSymbols.CleanupDebugSymbols;
  6364. end.