PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/imh/python3.13/lib/thread2.8.9
Viewing File: /opt/imh/python3.13/lib/thread2.8.9/ttrace.tcl
# # ttrace.tcl -- # # Copyright (C) 2003 Zoran Vasiljevic, Archiware GmbH. All Rights Reserved. # # See the file "license.terms" for information on usage and redistribution of # this file, and for a DISCLAIMER OF ALL WARRANTIES. # ---------------------------------------------------------------------------- # # User level commands: # # ttrace::eval top-level wrapper (ttrace-savvy eval) # ttrace::enable activates registered Tcl command traces # ttrace::disable terminates tracing of Tcl commands # ttrace::isenabled returns true if ttrace is enabled # ttrace::cleanup bring the interp to a pristine state # ttrace::update update interp to the latest trace epoch # ttrace::config setup some configuration options # ttrace::getscript returns a script for initializing interps # # Commands used for/from trace callbacks: # # ttrace::atenable register callback to be done at trace enable # ttrace::atdisable register callback to be done at trace disable # ttrace::addtrace register user-defined tracer callback # ttrace::addscript register user-defined script generator # ttrace::addresolver register user-defined command resolver # ttrace::addcleanup register user-defined cleanup procedures # ttrace::addentry adds one entry into the named trace store # ttrace::getentry returns the entry value from the named store # ttrace::delentry removes the entry from the named store # ttrace::getentries returns all entries from the named store # ttrace::preload register procedures to be preloaded always # # # Limitations: # # o. [namespace forget] is still not implemented # o. [namespace origin cmd] breaks if cmd is not already defined # # I left this deliberately. I didn't want to override the [namespace] # command in order to avoid potential slowdown. # namespace eval ttrace { # Setup some compatibility wrappers if {[info commands nsv_set] != ""} { variable tvers 0 variable mutex ns_mutex variable elock [$mutex create traceepochmutex] # Import the underlying API; faster than recomputing interp alias {} [namespace current]::_array {} nsv_array interp alias {} [namespace current]::_incr {} nsv_incr interp alias {} [namespace current]::_lappend {} nsv_lappend interp alias {} [namespace current]::_names {} nsv_names interp alias {} [namespace current]::_set {} nsv_set interp alias {} [namespace current]::_unset {} nsv_unset } elseif {![catch { variable tvers [package require Thread] }]} { variable mutex thread::mutex variable elock [$mutex create] # Import the underlying API; faster than recomputing interp alias {} [namespace current]::_array {} tsv::array interp alias {} [namespace current]::_incr {} tsv::incr interp alias {} [namespace current]::_lappend {} tsv::lappend interp alias {} [namespace current]::_names {} tsv::names interp alias {} [namespace current]::_set {} tsv::set interp alias {} [namespace current]::_unset {} tsv::unset } else { error "requires NaviServer/AOLserver or Tcl threading extension" } # Keep in sync with the Thread package package provide Ttrace 2.8.9 # Package variables variable resolvers "" ; # List of registered resolvers variable tracers "" ; # List of registered cmd tracers variable scripts "" ; # List of registered script makers variable enables "" ; # List of trace-enable callbacks variable disables "" ; # List of trace-disable callbacks variable preloads "" ; # List of procedure names to preload variable enabled 0 ; # True if trace is enabled variable config ; # Array with config options variable epoch -1 ; # The initialization epoch variable cleancnt 0 ; # Counter of registered cleaners # Package private namespaces namespace eval resolve "" ; # Commands for resolving commands namespace eval trace "" ; # Commands registered for tracing namespace eval enable "" ; # Commands invoked at trace enable namespace eval disable "" ; # Commands invoked at trace disable namespace eval script "" ; # Commands for generating scripts # Exported commands namespace export unknown # Initialize ttrace shared state if {[_array exists ttrace] == 0} { _set ttrace lastepoch $epoch _set ttrace epochlist "" } # Initially, allow creation of epochs set config(-doepochs) 1 proc eval {cmd args} { enable set code [catch {uplevel 1 [concat $cmd $args]} result] disable if {$code == 0} { if {[llength [info commands ns_ictl]]} { ns_ictl save [getscript] } else { thread::broadcast { package require Ttrace ttrace::update } } } return -code $code \ -errorinfo $::errorInfo -errorcode $::errorCode $result } proc config {args} { variable config if {[llength $args] == 0} { array get config } elseif {[llength $args] == 1} { set opt [lindex $args 0] set config($opt) } else { set opt [lindex $args 0] set val [lindex $args 1] set config($opt) $val } } proc enable {} { variable config variable tracers variable enables variable enabled incr enabled 1 if {$enabled > 1} { return } if {$config(-doepochs) != 0} { variable epoch [_newepoch] } set nsp [namespace current] foreach enabler $enables { enable::_$enabler } foreach trace $tracers { if {[info commands $trace] != ""} { trace add execution $trace leave ${nsp}::trace::_$trace } } } proc disable {} { variable enabled variable tracers variable disables incr enabled -1 if {$enabled > 0} { return } set nsp [namespace current] foreach disabler $disables { disable::_$disabler } foreach trace $tracers { if {[info commands $trace] != ""} { trace remove execution $trace leave ${nsp}::trace::_$trace } } } proc isenabled {} { variable enabled expr {$enabled > 0} } proc update {{from -1}} { if {$from < 0} { variable epoch [_set ttrace lastepoch] } else { if {[lsearch [_set ttrace epochlist] $from] < 0} { error "no such epoch: $from" } variable epoch $from } uplevel 1 [getscript] } proc getscript {} { variable preloads variable epoch variable scripts append script [_serializensp] \n append script "::namespace eval [namespace current] {" \n append script "::namespace export unknown" \n append script "_useepoch $epoch" \n append script "}" \n foreach cmd $preloads { append script [_serializeproc $cmd] \n } foreach maker $scripts { append script [script::_$maker] } return $script } proc cleanup {args} { foreach cmd [info commands resolve::cleaner_*] { uplevel 1 $cmd $args } } proc preload {cmd} { variable preloads if {[lsearch $preloads $cmd] < 0} { lappend preloads $cmd } } proc atenable {cmd arglist body} { variable enables if {[lsearch $enables $cmd] < 0} { lappend enables $cmd set cmd [namespace current]::enable::_$cmd proc $cmd $arglist $body return $cmd } } proc atdisable {cmd arglist body} { variable disables if {[lsearch $disables $cmd] < 0} { lappend disables $cmd set cmd [namespace current]::disable::_$cmd proc $cmd $arglist $body return $cmd } } proc addtrace {cmd arglist body} { variable tracers if {[lsearch $tracers $cmd] < 0} { lappend tracers $cmd set tracer [namespace current]::trace::_$cmd proc $tracer $arglist $body if {[isenabled]} { trace add execution $cmd leave $tracer } return $tracer } } proc addscript {cmd body} { variable scripts if {[lsearch $scripts $cmd] < 0} { lappend scripts $cmd set cmd [namespace current]::script::_$cmd proc $cmd args $body return $cmd } } proc addresolver {cmd arglist body} { variable resolvers if {[lsearch $resolvers $cmd] < 0} { lappend resolvers $cmd set cmd [namespace current]::resolve::$cmd proc $cmd $arglist $body return $cmd } } proc addcleanup {body} { variable cleancnt set cmd [namespace current]::resolve::cleaner_[incr cleancnt] proc $cmd args $body return $cmd } proc addentry {cmd var val} { variable epoch _set ${epoch}-$cmd $var $val } proc delentry {cmd var} { variable epoch set ei $::errorInfo set ec $::errorCode catch {_unset ${epoch}-$cmd $var} set ::errorInfo $ei set ::errorCode $ec } proc getentry {cmd var} { variable epoch set ei $::errorInfo set ec $::errorCode if {[catch {_set ${epoch}-$cmd $var} val]} { set ::errorInfo $ei set ::errorCode $ec set val "" } return $val } proc getentries {cmd {pattern *}} { variable epoch _array names ${epoch}-$cmd $pattern } proc unknown {args} { set cmd [lindex $args 0] if {[uplevel 1 ttrace::_resolve [list $cmd]]} { set c [catch {uplevel 1 $cmd [lrange $args 1 end]} r] } else { set c [catch {uplevel 1 ::tcl::unknown $args} r] } return -code $c -errorcode $::errorCode -errorinfo $::errorInfo $r } proc _resolve {cmd} { variable resolvers foreach resolver $resolvers { if {[uplevel 1 [info comm resolve::$resolver] [list $cmd]]} { return 1 } } return 0 } proc _getthread {} { if {[info commands ns_thread] == ""} { thread::id } else { ns_thread getid } } proc _getthreads {} { if {[info commands ns_thread] == ""} { return [thread::names] } else { foreach entry [ns_info threads] { lappend threads [lindex $entry 2] } return $threads } } proc _newepoch {} { variable elock variable mutex $mutex lock $elock set old [_set ttrace lastepoch] set new [_incr ttrace lastepoch] _lappend ttrace $new [_getthread] if {$old >= 0} { _copyepoch $old $new _delepochs } _lappend ttrace epochlist $new $mutex unlock $elock return $new } proc _copyepoch {old new} { foreach var [_names $old-*] { set cmd [lindex [split $var -] 1] _array reset $new-$cmd [_array get $var] } } proc _delepochs {} { set tlist [_getthreads] set elist "" foreach epoch [_set ttrace epochlist] { if {[_dropepoch $epoch $tlist] == 0} { lappend elist $epoch } else { _unset ttrace $epoch } } _set ttrace epochlist $elist } proc _dropepoch {epoch threads} { set self [_getthread] foreach tid [_set ttrace $epoch] { if {$tid != $self && [lsearch $threads $tid] >= 0} { lappend alive $tid } } if {[info exists alive]} { _set ttrace $epoch $alive return 0 } else { foreach var [_names $epoch-*] { _unset $var } return 1 } } proc _useepoch {epoch} { if {$epoch >= 0} { set tid [_getthread] if {[lsearch [_set ttrace $epoch] $tid] == -1} { _lappend ttrace $epoch $tid } } } proc _serializeproc {cmd} { set dargs [info args $cmd] set pbody [info body $cmd] set pargs "" foreach arg $dargs { if {![info default $cmd $arg def]} { lappend pargs $arg } else { lappend pargs [list $arg $def] } } set nsp [namespace qual $cmd] if {$nsp == ""} { set nsp "::" } append res [list ::namespace eval $nsp] " {" \n append res [list ::proc [namespace tail $cmd] $pargs $pbody] \n append res "}" \n } proc _serializensp {{nsp ""} {result _}} { upvar $result res if {$nsp == ""} { set nsp [namespace current] } append res [list ::namespace eval $nsp] " {" \n foreach var [info vars ${nsp}::*] { set vname [namespace tail $var] if {[array exists $var] == 0} { append res [list ::variable $vname [set $var]] \n } else { append res [list ::variable $vname] \n append res [list ::array set $vname [array get $var]] \n } } foreach cmd [info procs ${nsp}::*] { append res [_serializeproc $cmd] \n } append res "}" \n foreach nn [namespace children $nsp] { _serializensp $nn res } return $res } } # # The code below is ment to be run once during the application start. It # provides implementation of tracing callbacks for some Tcl commands. Users # can supply their own tracer implementations on-the-fly. # # The code below will create traces for the following Tcl commands: # "namespace", "variable", "load", "proc" and "rename" # # Also, the Tcl object extension XOTcl 1.1.0 is handled and all XOTcl related # things, like classes and objects are traced (many thanks to Gustaf Neumann # from XOTcl for his kind help and support). # eval { # # Register the "load" trace. This will create the following key/value pair # in the "load" store: # # --- key ---- --- value --- # <path_of_loaded_image> <name_of_the_init_proc> # # We normally need only the name_of_the_init_proc for being able to load # the package in other interpreters, but we store the path to the image # file as well. # ttrace::addtrace load {cmdline code args} { if {$code != 0} { return } set image [lindex $cmdline 1] set initp [lindex $cmdline 2] if {$initp == ""} { foreach pkg [info loaded] { if {[lindex $pkg 0] == $image} { set initp [lindex $pkg 1] } } } ttrace::addentry load $image $initp } ttrace::addscript load { append res "\n" foreach entry [ttrace::getentries load] { set initp [ttrace::getentry load $entry] append res "::load {} $initp" \n } return $res } # # Register the "namespace" trace. This will create the following key/value # entry in "namespace" store: # # --- key ---- --- value --- # ::fully::qualified::namespace 1 # # It will also fill the "proc" store for procedures and commands imported # in this namespace with following: # # --- key ---- --- value --- # ::fully::qualified::proc [list <ns> "" ""] # # The <ns> is the name of the namespace where the command or procedure is # imported from. # ttrace::addtrace namespace {cmdline code args} { if {$code != 0} { return } set nop [lindex $cmdline 1] set cns [uplevel 1 namespace current] if {$cns == "::"} { set cns "" } switch -glob $nop { eva* { set nsp [lindex $cmdline 2] if {![string match "::*" $nsp]} { set nsp ${cns}::$nsp } ttrace::addentry namespace $nsp 1 } imp* { # - parse import arguments (skip opt "-force") set opts [lrange $cmdline 2 end] if {[string match "-fo*" [lindex $opts 0]]} { set opts [lrange $cmdline 3 end] } # - register all imported procs and commands foreach opt $opts { if {![string match "::*" [::namespace qual $opt]]} { set opt ${cns}::$opt } # - first import procs foreach entry [ttrace::getentries proc $opt] { set cmd ${cns}::[::namespace tail $entry] set nsp [::namespace qual $entry] set done($cmd) 1 set entry [list 0 $nsp "" ""] ttrace::addentry proc $cmd $entry } # - then import commands foreach entry [info commands $opt] { set cmd ${cns}::[::namespace tail $entry] set nsp [::namespace qual $entry] if {[info exists done($cmd)] == 0} { set entry [list 0 $nsp "" ""] ttrace::addentry proc $cmd $entry } } } } } } ttrace::addscript namespace { append res \n foreach entry [ttrace::getentries namespace] { append res "::namespace eval $entry {}" \n } return $res } # # Register the "variable" trace. This will create the following key/value # entry in the "variable" store: # # --- key ---- --- value --- # ::fully::qualified::variable 1 # # The variable value itself is ignored at the time of # trace/collection. Instead, we take the real value at the time of script # generation. # ttrace::addtrace variable {cmdline code args} { if {$code != 0} { return } set opts [lrange $cmdline 1 end] if {[llength $opts]} { set cns [uplevel 1 namespace current] if {$cns == "::"} { set cns "" } foreach {var val} $opts { if {![string match "::*" $var]} { set var ${cns}::$var } ttrace::addentry variable $var 1 } } } ttrace::addscript variable { append res \n foreach entry [ttrace::getentries variable] { set cns [namespace qual $entry] set var [namespace tail $entry] append res "::namespace eval $cns {" \n append res "::variable $var" if {[array exists $entry]} { append res "\n::array set $var [list [array get $entry]]" \n } elseif {[info exists $entry]} { append res " [list [set $entry]]" \n } else { append res \n } append res "}" \n } return $res } # # Register the "rename" trace. It will create the following key/value pair # in "rename" store: # # --- key ---- --- value --- # ::fully::qualified::old ::fully::qualified::new # # The "new" value may be empty, for commands that have been deleted. In # such cases we also remove any traced procedure definitions. # ttrace::addtrace rename {cmdline code args} { if {$code != 0} { return } set cns [uplevel 1 namespace current] if {$cns == "::"} { set cns "" } set old [lindex $cmdline 1] if {![string match "::*" $old]} { set old ${cns}::$old } set new [lindex $cmdline 2] if {$new != ""} { if {![string match "::*" $new]} { set new ${cns}::$new } ttrace::addentry rename $old $new } else { ttrace::delentry proc $old } } ttrace::addscript rename { append res \n foreach old [ttrace::getentries rename] { set new [ttrace::getentry rename $old] append res "::rename $old {$new}" \n } return $res } # # Register the "proc" trace. This will create the following key/value pair # in the "proc" store: # # --- key ---- --- value --- # ::fully::qualified::proc [list <epoch> <ns> <arglist> <body>] # # The <epoch> chages anytime one (re)defines a proc. The <ns> is the # namespace where the command was imported from. If empty, the <arglist> # and <body> will hold the actual procedure definition. See the # "namespace" tracer implementation also. # ttrace::addtrace proc {cmdline code args} { if {$code != 0} { return } set cns [uplevel 1 namespace current] if {$cns == "::"} { set cns "" } set cmd [lindex $cmdline 1] if {![string match "::*" $cmd]} { set cmd ${cns}::$cmd } set dargs [info args $cmd] set pbody [info body $cmd] set pargs "" foreach arg $dargs { if {![info default $cmd $arg def]} { lappend pargs $arg } else { lappend pargs [list $arg $def] } } set pdef [ttrace::getentry proc $cmd] if {$pdef == ""} { set epoch -1 ; # never traced before } else { set epoch [lindex $pdef 0] } ttrace::addentry proc $cmd [list [incr epoch] "" $pargs $pbody] } ttrace::addscript proc { return { if {[info command ::tcl::unknown] == ""} { rename ::unknown ::tcl::unknown namespace import -force ::ttrace::unknown } if {[info command ::tcl::info] == ""} { rename ::info ::tcl::info } proc ::info args { set cmd [lindex $args 0] set hit [lsearch -glob {commands procs args default body} $cmd*] if {$hit > 1} { if {[catch {uplevel 1 ::tcl::info $args}]} { uplevel 1 ttrace::_resolve [list [lindex $args 1]] } return [uplevel 1 ::tcl::info $args] } if {$hit == -1} { return [uplevel 1 ::tcl::info $args] } set cns [uplevel 1 namespace current] if {$cns == "::"} { set cns "" } set pat [lindex $args 1] if {![string match "::*" $pat]} { set pat ${cns}::$pat } set fns [ttrace::getentries proc $pat] if {[string match $cmd* commands]} { set fns [concat $fns [ttrace::getentries xotcl $pat]] } foreach entry $fns { if {$cns != [namespace qual $entry]} { set lazy($entry) 1 } else { set lazy([namespace tail $entry]) 1 } } foreach entry [uplevel 1 ::tcl::info $args] { set lazy($entry) 1 } array names lazy } } } # # Register procedure resolver. This will try to resolve the command in the # current namespace first, and if not found, in global namespace. It also # handles commands imported from other namespaces. # ttrace::addresolver resolveprocs {cmd {export 0}} { set cns [uplevel 1 namespace current] set name [namespace tail $cmd] if {$cns == "::"} { set cns "" } if {![string match "::*" $cmd]} { set ncmd ${cns}::$cmd set gcmd ::$cmd } else { set ncmd $cmd set gcmd $cmd } set pdef [ttrace::getentry proc $ncmd] if {$pdef == ""} { set pdef [ttrace::getentry proc $gcmd] if {$pdef == ""} { return 0 } set cmd $gcmd } else { set cmd $ncmd } set epoch [lindex $pdef 0] set pnsp [lindex $pdef 1] if {$pnsp != ""} { set nsp [namespace qual $cmd] if {$nsp == ""} { set nsp :: } set cmd ${pnsp}::$name if {[resolveprocs $cmd 1] == 0 && [info commands $cmd] == ""} { return 0 } namespace eval $nsp "namespace import -force $cmd" } else { uplevel 0 [list ::proc $cmd [lindex $pdef 2] [lindex $pdef 3]] if {$export} { set nsp [namespace qual $cmd] if {$nsp == ""} { set nsp :: } namespace eval $nsp "namespace export $name" } } variable resolveproc set resolveproc($cmd) $epoch return 1 } # # For XOTcl, the entire item introspection/tracing is delegated to XOTcl # itself. The xotcl store is filled with this: # # --- key ---- --- value --- # ::fully::qualified::item <body> # # The <body> is the script used to generate the entire item (class, # object). Note that we do not fill in this during code tracing. It is # done during the script generation. In this step, only the placeholder is # set. # # NOTE: we assume all XOTcl commands are imported in global namespace # ttrace::atenable XOTclEnabler {args} { if {[info commands ::xotcl::Class] == ""} { return } if {[info commands ::xotcl::_creator] == ""} { ::xotcl::Class create ::xotcl::_creator -instproc create {args} { set result [next] if {![string match ::xotcl::_* $result]} { ttrace::addentry xotcl $result "" } return $result } } ::xotcl::Class instmixin ::xotcl::_creator } ttrace::atdisable XOTclDisabler {args} { if { [info commands ::xotcl::Class] == "" || [info commands ::xotcl::_creator] == ""} { return } ::xotcl::Class instmixin "" ::xotcl::_creator destroy } set resolver [ttrace::addresolver resolveclasses {classname} { set cns [uplevel 1 namespace current] set script [ttrace::getentry xotcl $classname] if {$script == ""} { set name [namespace tail $classname] if {$cns == "::"} { set script [ttrace::getentry xotcl ::$name] } else { set script [ttrace::getentry xotcl ${cns}::$name] if {$script == ""} { set script [ttrace::getentry xotcl ::$name] } } if {$script == ""} { return 0 } } uplevel 1 [list namespace eval $cns $script] return 1 }] ttrace::addscript xotcl [subst -nocommands { if {![catch {Serializer new} ss]} { foreach entry [ttrace::getentries xotcl] { if {[ttrace::getentry xotcl \$entry] == ""} { ttrace::addentry xotcl \$entry [\$ss serialize \$entry] } } \$ss destroy return {::xotcl::Class proc __unknown name {$resolver \$name}} } }] # # Register callback to be called on cleanup. This will trash lazily loaded # procs which have changed since. # ttrace::addcleanup { variable resolveproc foreach cmd [array names resolveproc] { set def [ttrace::getentry proc $cmd] if {$def != ""} { set new [lindex $def 0] set old $resolveproc($cmd) if {[info command $cmd] != "" && $new != $old} { catch {rename $cmd ""} } } } } } # EOF return # Local Variables: # mode: tcl # fill-column: 78 # tab-width: 8 # indent-tabs-mode: nil # End: