From 5743b481019e3c5009a7e6d0948a5a75fc87b890 Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Fri, 18 Apr 2025 15:15:17 +0000 Subject: [PATCH] GP-5264: Add PC, Function, and Module columns to the Time panel --- .../DebuggerTimePlugin.html | 16 +- .../images/DebuggerTimePlugin.png | Bin 20564 -> 16126 bytes .../action/RegisterLocationTrackingSpec.java | 8 +- .../gui/action/SPLocationTrackingSpec.java | 6 +- .../gui/time/DebuggerSnapshotTablePanel.java | 41 +++-- .../core/debug/gui/time/SnapshotRow.java | 139 ++++++++++++++++- .../modules/DebuggerStaticMappingUtils.java | 19 ++- .../time/DebuggerTimePluginScreenShots.java | 141 ++++++++++++++++-- .../GhidraClass/Debugger/A5-Navigation.html | 9 +- .../GhidraClass/Debugger/A5-Navigation.md | 4 +- .../images/Navigation_DialogCompareTimes.png | Bin 15382 -> 15380 bytes .../Navigation_TimeAfterCallSRandCallRand.png | Bin 25332 -> 25533 bytes 12 files changed, 336 insertions(+), 47 deletions(-) diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html index a8f8beeaaa..6ec4618341 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html @@ -38,13 +38,21 @@
uRfxp%H5u`H2b^sR`%y z3PtOgaOR0`bCf@{aYjG1|NVMcv^1E*-kfnKM2qoW2<1O7t$b>vQsOhYFh;SjHd9qw zQ_eFFqu4X`;RUC=Yn20q6GVLTctSpF?W^&})}A!uITbZC715K}H>xi^ITl{Qr2lzE zleAjwEdCg%_x3g)$_*bv8R$)W?i!)K}QeNfSb+)8y8VdIp+1-~u$*QMT|U2ch0j?dFAw`duv zakV!e&X3M{9ZDbn#;vR-SW8zoVP|DbW4 L zzD9+WJ{QciAhJm2N$f+Ub9)R!waIqq)DqMD&83ssJ2X*V#awZlwL5RsrPp$|9er-U zKmLYSKg}rq*CkH?^JmOx38GTiUz^GTzKQSM&lh7Q@F1(Hs;Ai@bc12D-SH((tGZ9? zttTq34QArt1&Z4y37{ghLVED93{yRK(BO^1V@f?-S88mYu=zw~S4KG=Zg?nAhIbC{ zm5OOc$AfS=eKQe)cmYM8D;00EOW0{a|GbczJYZcONapJhb+*pnrdLWy&((HWPqwO5-18K$YvyVASnR;*Om9C zAcj-tHz+Ro;|;p}Hox8F;mF4t*{pHg8eXj=d_B=%Y7GK^KCr}&hsHofLHPb*cT|Ww zx%~Jk3*vq)G})Q9Yr&8_MmL1EE395QVr+t3e)ndi$?%rkmRv0f@$2s~<{A3DJQmaU zFqODh@KEtTUhyl_-r8O%)j}s-yKk)q2CR$Wx*nCUybO}qoGat*3kuMKd~>Y7tYtzT z7O+cvY7PA7#X%XY70IULK`>){)5^9#?Sorn|1&XN$ySn=D$(WzJhoW7$5F+HYmpF> zuI#J -bn1ng$XR*p9J0&(k;e9q*P+OiWwb}+QYU}Tj-r*=Dy> Pdvtv}*iJ>c|cH}+qy`Xkhg6L{vB_i&a&7gs=ig>wY-e0iQ z?0jCGJ-+|8W}n$k7Z7Gz{;Dr)m=)hq?@4$Hx+k+9o$~RZL@6vLw)n8gtBoXIy{LA_ z`oOO{aa8TKQVK_pyJA$rYHOFCbz6ssL2Ji`9SlC+cjK#l;ea}3{)m|Ed)EEPyP9i( zzY4#z&2Xy23ilXAIZGkyQL%2_%KyUzZ2W41w|`;geQGoL{8>uAUZss4T$e|`;v(#M zFLD=2X)Sv^Vz7{4_7%~JSdtKogl~(-!tLEVqkiq0d_Tf{wceG =j@7z^BO09q)_1z`JE6-+}(ekj+7x<9JI(4V1FY1 zwMWnY#o_$9m4iI!-+FM~)-kAkE<%By4p4)32o3+oe`(Y}dIDSeTQ3nrHH@>rHt)0U zzsHBSa$Hl?_nTvM5o>Llm6Tok(8|I3|Fwa+j}95iAR-^lp%vw{Iu56jA*7&U#t=cn zHaA>uMYy+bw~~A$`Pqi9uo^RteoaIH>D`R2Z&nt0`LY4e%g7jZEaS9o>=etc!){0o zipy8el9wygEg#xRcxt&~G4}B@2> g zFxKv}&l{m(-t!AbGANPr59;o-01i7o>u~|s*$~0GB>UcQTvsepVVuGNOW)Hwigi5T zRPR$)#iE+6KmnUcZ6$8Kif&X2JPw`F5PhkeI-A#KZQc8<`Yxxo>gir(eaC1qEK4Sg z5z+B5ww;(g>3-!TBB(o6LZYHOoJHoVTqGN!fq<&rJSUkA2s;`bYW&8{*D-tJmRjzy zhF+MRr9qjN9!4^Ix_eyukHW yc!3y*$wkJ`rt+TWwL_1q5;3ndSG*!n^ zPAXSX-C;ic^LO{b$`LFpyNkjz-+|Sh_4Y?bL`+P~Es5=Jel5|(nwJx`Zo3g66#AvI z=;o{4_`Vb|5%9clB_i-!n=g9vFql^0?JYW+D?Yu%Mfz87G6;Vi2dQQF-35UaaaV7Q z%#>R~gw|`eHH+dy<8jb~CcHj>&i?ugw{6`)w(h!)S#nnPZGOC4au=)XmVfa}^60{~ zscMDw>Dtw09bh(T^@+Qc3E^Ea(GL}3a{Y#Zy(5H+(wO@dz!a0E5Zh`Dq*`WH@z1Qs z+r{=LGlK={ZrLeo7D2_pUVCF^UEe=kh0oM^cCKg^X!C;S$5W7~DtSPXJ$Bz;i(UnE zhGkJLTc5<4M}a74;uCzWb|1PMN82dCs25pqd(X|mvo0YP>L#aKVMRk_`6I*sc)$Kk zYfy55a@=b!Tt+jDP~3j9f68gX+79P^*C>q0Zgy9vP#>cne_N?v=vS;|!ZT|Q=PAe7 zPC7icC>EG0A@IlDSP(>c1Z6-+#h-Js6<~SPLrumIqf_fve&=JafYm4u3N?dKj9@XO zMgr6R@OmxLb0HpvmN*a_>g{#;OL5P_c2mUMHOW4=2mcCH)hIEM)A{yB(#osp;|;{= z!9v@75EVDq>Uepi782ZIOpDzcT^TKwnv%09DG7O$3aCOWuihjf1dyt>&C4PDY8mY& z4X*t8Ewz5x?arlBsLL<^65|$ NR%x$ZGp4v(y;9Qk@&=FeZ(2SRS$%9BW8WJe5lo->kzz7`067!b;B;*2wPIhQ zAosJdYMDK4c=9)FCf=TxM%)8E>4q<~pq-V^R!L<9kqQ^-TPN~jG0_?qy~CGYbMXV0 zTMUnS^b4ii;b^I!)H8w-O|}An?GZ~p2!qcChQFJWhrzwmytaA3i{T`hozWn=LRKIk z3XG;^F_3aNw~SX EvDZ_RaB)I<`(s5bmRR|~L;Y~S1o=I{Napna+1U4NRS z#XI&^MWg%fPL;O@fn5_C5{^DQJ(_KO{$=j3@uaKqc;6=YJV4Q>7X+Cw?RK=me48Ln zaK63ga!YWYx794f CuMo4mSuhf%x?tRhupqkT0SUT=<=uzASmlx>;a)xq+Io#`@D&i@Ip3^LX`X zb}fQ R?BDeBf>lp%ZK!CKYBsM%-w% z94^#@gkb%CEf3{s6m;*>B=RaA;mENVQ{|?`+S5b)a>$G8gY%gUX}iGkh;HSe*%%ig z)1Zzos$?Ujpz64(sQ#nzue0dJw#@Vc;6#%kVdIKmh@;N*$HGKK84u{@!)gdZv}EJZ zvQBS|gYR9OZgaMA5?l8Kz|2O!AF%F!KmP>smk9Fz7Y%i-B>w*ZN-nOn$kikNzYcQw zmjd9w+HV8u M=p=wdFZwt-|Ri}&qSd&kFT-$nLzq;lC ztn}Dp++6+)sixPm;+B_@RQI#LF2 vVKU75QeAfVL+q;n*JQKnps1JrPZdN9M5IUv0^_Wy_+-c+!~1MpXtmDB@jpY40I| z1znY_{Ju_@3iwM4Qjo(}?R1kpODcAac(=v@Z|y8w|G_u9_yyf?inp44>|;6*`C-92 zx-@EM?JSy8yAC{JHT~F;Rv^ZE+R?e-I6Kicy3!J>L3k@4S{fQSHgS9S@OVzxOSiB7 zPoRKDCB~Mau&h>Q&WDscVqe^<=+t q;eDGxx_iDY7TW;E*NM| zBu }v{xO+cYH}{LECaQ+-iRo1D zlzSdyh!1>t@FhPCE0`k2BM6gh%{z5dT!{5GyJh%4sC#J=kY?`}&PbLWQ9VHKTC6n8xh zEhO}r+ui}6QT3*d_lWb0-1A-|lAS;OYQ-hl_eH`&g JzvHjmEO0q5b( zKd*O xslfctj1 zE)YF&<{H}_SNZB5N}s#$WC5qGQwp{o%5>X#R-~QErlM8wpr}qq%&1n^C20mZ^TJWX zmoBL-HJ`fD+UX?C+I}?*nVh!T{3Wn@tqwBJt;ez*g93};P)?6J73AoUR|rVj=M9Kp z+i|Tf6xG0DS@)45zp<9mT_yY4&C7F~(XrB$dm+M7jFLkunjHiNe L0vl6njXJR_ zrzlHa%u09;v$0}4!dnDn)I^mP%V$HFDNUg7GFw&DU|#*T1@*^Du%A`W4R~YqcVmhs z^1JVjIM2tM$@#YSXzte-aLX>LN&3(}Gj`AyjqjtCyXov^8b-Bt$b;&nhxy0VT5009 zBk_L>HupiefN+>6KF;mL+qO*{qG7996yD$3%5XHyaFCO_tL4qhWXf <8{;PFsh!!mtyl>n*Z#du~?*6B2{AQbI;IH z>x+%H?7NrjF*7OuG@gq7&eiGDm+RN+-tqSHEjBv0=p%-kU@t^Tq qMt;gm>!bCR*}Q%``^wY-_4b=kAs@9yG{+Qd7hG6X_xf=)-nV8C3u3Ie z*{bnt;lavtKRZaW_$tll>o`PUCo!g11j6|!ARR&vD^N8JE4DcB(i>(8@$fOa$ =TYvSMH9O(L?55a9z8AT8i^PlN(l|xH=(#w< zcDO!Key7S%LvEfRK(P6ppu_QI8>q==^Qt6O9mq6xVgR1Hvsef{kl|L%b6?Yt>(t*1 zP@DA#37$gTo34;@?KcZaz56}UZZX~`uc`0pPaE}EmBdonwc4}GyM*M|uKGw8?K!=L zJA1mNuDI&$xz)xz^-q=OaUWD%S|zbyQ53q^2+5#(QB6E+w*_UXg)g!AB{Ca)jOABl zg2jD_eAn}HL4U>Zt> N^$zMmk8GwhRe^n}PkWMZ>F6p|cKglco z(1xomhStqj;l+p$>gVzx+6}-A7ylb5`%l3Dz=7Kr6|K3n;s@u&tk2hJK^G05{!4?L z Lh zK-PGNaNP!nfQuH(>D z)9v{l^%@tu8DTa63`D@f#K5|hHr7;m&rs;!f#_AjxccL*a~)xFqiB|HUUUQ06%==5 zr4YM7cw7NH+FRuy*9Wq2bs(oYPh96V+n8eQW?S 7Z17`9(jor$RCyjjUZ0(yOF&_uhtgD?*u>1-x)GaX|%+_Xz@NSPN9*ARC&fk(@ zxQca4@!ZqNQ%(1=nnbJ71hc1ls8>0!+d#JGLPgfjwJ`|x;ThCpQ~I%9g;ha~{X#Ed z$`RX90`H3AnF6W|+OB!(S#!vux(MKx&fa8^ETD^s{VC_RPTSgDa2}*6h3~y;vaq9O zchCx|j~u%>ShLj$8UU&iGr|chGNJ9#R0ga-0u1rywe@*sgZ@Q$us2~l``efBhB|Pp z-Pd7X{Bsz66peG#Kbj9bq4{G?WOEGUh6FlgZ^Kt+>J4MK4Y>fqftmE!?s3vG69JOW z7~mw48ZR?v0dif);ZI+3O)@yG{n%5>k#-6dQDy-7bgE-WOFjMroTweW+M*Hwk2Q-D zl2UO+0ZAXAc_=wFWjkZJixh)&Dj`MCe3{v13Y9258;tanl7E9^fr`Y51N@T!52SL& zt6Ggk=neRtVAcm>+GkeUR*Zv{Q%-tT)23JyAbkYlUMD6nh&VMzPXH_AyJOOOh{x&i z!Fm4MRp)smijO0s;d@0R{tn @9Uc;< Tm7zu*u|CxE}amKG9Z_ zJBE<0OEFM87IHM2dhdyD^R7#J7b7uQ+Z8eJE*=Zv L${(QaYc5n|ywFPs u8vfMba|hZnoI zEc>^8+cp=~lob>-veSU%uXrns{(7YSdhLEbG<6sWK| e=8u07(6}QSur^bwie6&-o zz&3n9E^+)Vp>;Nx%b=<`Ix>m*U)$hbRG4a=aZBK+=p&tyubkT7?mr7lq0`4gM}= f!%)h%`p{D5Sb^zdIT9%$r48$C@W^n1MDgrpY(x0ADaOTwa+ zx?%6Di_a+uO-fS$ITD2^oph|x=cO`Qc3kXdws`@kKHOg~B2-mQ5a{5Nck8C;?v=Yx zI(K^iQabM#B=~g_5b5qYD<86*xMMJ)j9R}@Xn+D1$3c!*X?;SA3^9f}(6(}0NnXeS zJk}uQ-Ho_wxEE0uEQUODPB)ARE!ULT@C@a|0WMe+Y9*PMoz)#-&FTC>7r_3neu~_$ zIq{z+_V{Id%S<)nVyKyUf2vVOKme{Zq(T@swe!6^iE#FzeAJ`cv5Shrj>~gLF^q)S zzi&TKYys*&LoOuS%6DE9&*nRXX-!o*dWNVwVTsP=j?Hff(QMv^{cSFun_tcPQY^-c zZrsvTE|KZq+y(7}y71_|Cy(YiY$qxTmmOdCe$Q;`h-7bLboB)Wo#lhfEH&Gu^e0$1 z0hgm9=k@9QCuANn(C-sn=y@ys)%#4woOh?M3CxhZBD=c!6a`nx;B2##Vszi`5j+6D z5r3%AO2YoRP_t#yxrX_ph_EPsfRQ;64c`E_sJJp~H6qj45O;Ow3IN(XmyMq`yjPK6 zfmI^?D_1WuUj4}%d& )d&rBTY9()zK@`?$=1jJ_(^b!3@WidX+I2WCgrYCg?jD|`{5Z~%K7c* z6{7?Ct8X-no&d*`onuYRu-{|n4z9L|=s!=M1k*Idq7J}<9TFxT+TmpVq`ud}^&02| zU8DlOXfiL7_J^%-DqLr#rA2c3*q9$*5p)Z@&WyPVv6Z}~qx13B%Nii?JXl<=1K5w$ z*>`qkK>ln=2!JmvJ+~!c(ty$D)}7latrkaK=@+^wBjQ#F @}TR?yL5lXz6QVrw&YGcZsdNa@x;hDEurfu7_fx#iyl zIaO$S?g{Bs*eJZL)l^evC#e5g|33p5l0(kQ)gyyx4`lXTaX>Jg4d)gbS9xsgv&{9X z@3(iHE2biC9F0zyg;c?D<$(h30})fiOMg<35iPH~NQQ2zf>)c0< @WSBeL|O5^?b%G9>DK));K8js-*xyfs9a#(H&*BSL*1&+c;_NmrO2={miDX$45 z8~)SOTK+y!1xEK0pKiUXah)95@WCvH_!2(r-UntFPclUU1MK{9k%n0w{ZJY1h#3F< z*#qv0nIMYno|q%~*zibYaUJ5pw{4bUadrErM0XIMb^4~+>rYB?khbZYCs>8OtvuA% zkm170DdrQxn_$D&K@i(0i@y3?WfLF;UJMh?)5qs&?&?IFYzTdS6Xks@x745I{!)ya zz%z!)Yf=YQw (ny+`xtEC*`V `7?_-ovIwcfVl$r*Y|hsZP=Kqq#ttwo-t zt7&*$l`{cTA3hbpjbInkXISx?(RGE`HRENLw$ 7Ws{>f8I`s=QH{ z@rIpQ6)nvP6PqA|KJIz;`ole^KhYYW0h~A1NJZ}^y5f@GF52+_1SM4k6;C*HI6jzN zBl{@Z*Q6g$%&s$*`Y>YfeM6J+c3d59Wa3WDpl&rhcAQ%*%)Bgb0b)CT8)5?jvOE2@ z4sLPRc@NA+Mi9Jd&ojkQ(UbS>Cq9q3y0wpVxGvj|t ><^6f z{Ik(wExO7sVggdP5y{?7kn`hi$B_CNvLm@(VPqT`?O|rAJ}HL!K&MYar?_PM^UFfF z8@Rkct)$gAw1Qu~Yv eU`r~iJG0Gsz3Wc1anS-Ix40g zLH9|VR>qq!ULFVf$5;iEKisogR_$S#L!$p}O-UYH$B~~Rla?P2@a~|0+KIYFK?+nf zvVRT>BHKEB5%;@W44h|>ZjKwz$!q&j*8G){Dzk@@Dt+EFsE3tPb~&ZJTaJ2KDVWpN z#3US)#NHsY^0?;rLz5jpbK !}MBD^-Z>@ zc*Mo1l`}wbni(bVf>y;O)S79lazeOj49Av8L%)>oENn#DxPKczxYs P>fJalp6;- zVHeo3n@tAncfK(;{mh4v%r^BqhD|@|p5>n3d rVI2s)~t`68rmZd2GFLbcr(L@s<;1 zJLNVcVsMI_zRd#PVilesC{DLZbj2}blEfZvo5_V8Y`3G%xV%KY6=ySZmn=8Fw8yD? z3469*K`Z}@V>{@eL!7bVc-*liH?y;(*e9r!fZwWl(XT`^!qq2)Rzf=DpuxXLMR|}K zt^69&{SYEHncSdg ez;g->*rVn8nY2 y }N z5^P|Y88x*l2|dD7lIs!8asjH)iN5hquyX1inH2zwqQwaQkzsNHSyqca?#YZT(k(fh ze`E389J-@eW?EJCW;30;i$JK+m0RP8!Zu5~9X)UAx9eI&joQjJ@D(jobTt0(D77tj zvv8hn)C>RqODyj^iS3mH033b1e1>w;xBmTS*348Nj*3e?UM8{*0KI0=0{A-7(8HQ^ zI97=uo}GG{wSi#@$VQ6D6p-ZO@S|(xVWm1}k&Ly&$c9A%4#z&vPKUUzQyzdY(eJh$MGebGdVbTngZbt9Y zCpIS^)Y$n7I;=dG#zElhzs$^ZfEw8oKsb{45^pNh{>9Y}?M?P&{nWrQfEwHaEUWkY zQEQf{YVjR%k8{8_#Po3cV?&HVY#`wC`)~C)*T1mZtqA)6rZE3Y!{h7F>b@IuF{}4k zIo=jrXFed@u5@dRh=y-n#lXGoyPR&_p#+rUZI;*P&Igv9Bt+l^N9*SAYX_`^D;AHX z{!)(C6VF@^mvXXX!yl*vUZZpOt3gXF;3|;^d~3KN4Hd0aoPD1dJ3yK~M+0Ty_2uU0 zt7U^x7FblFaYCBC5%6bQ{Ziy|zZ(6cc|zjOvrE$Y*_^Q&*V4P((8 dW)o0BP$7SW*-I&qpEh dvdN`iRfx z4wzn%bx~J<(84FtcKMUj+*t9due>m`WXG7!9MhX*#sII`0?d8+^uaX_6V{a-=a^=> zJ4H+n$d!TmqDRS{Y_pyON(d32JW zOYgraL`0u_5;ez znHQ8201>J4(GX}jU OS0O5b-|k$1EEjilV|08ljnd=Mpo3-RE1yAKC&pbDh$gk6hb_tw9GH$Dp& zUu&OYSHKty$8|=Rz}jxoh`E)GoA&%}3BmzZfiLF{n!{auwD_N&S1G09+2U(%m-ToZ ztw2nF=Pv}t$Kk4-*JA*b*+FMp!@Q7WA9^^c{`qT2bPvj)dJbT7WlfKLUHP8qqg5H* zS9l%w)pJ#u9~;(P6fL2(Zh0aU^raX0uE(#zQ9ae`aH;c_`5Tl?{fSfNYsB}0pUP5K zDFFA&w^7k-JDGWZPp&t#))rq}j(ekldL=rPVk2-ibo+M@cMM>g1Im>&d?tXv{Mkxp ztB4knrZe<9fHykLmNN3Z0ZNr$3Ml>|E`?E^q@6O4+Cpx)eeNf{{Ep?vS2+sIPajgv zA_U;S7>dnhdql+e{N0PG_B48|L0c$8@ F!)<4=n=O8(-maH()e( Y__9uU~8Zv`6XWNjYxoZE=k0r`~OM`Rxz?5F2k3r-IX#1v6iH6FAApnQ^ zVPgR>TLtIg)@0Isy*57w4mgtYsm1}WrEk0Vxh+$oY$*WA0O@Y#lDXOPEs!UzveWkM z)fdTtKWo&85(F;Lwi3TsJWiO;hVh?~DIn9|TA|aT_J$fCIO#z2uNHv_6^tKHd|IQ- z_MVY;a@Aaw%Ag@Z%zZCcq=W}OqET+4 G0sGdmworBtt2MwQ4t@!HNu2)4oOBpeDz>|E4!$xL8)eq- zeK+i$Rd}5_@n ;C`YANbfbz|IqWnkCTcJ zWyD&nO=1A)_ytp; oiZX^i#t0G8;5azaFeA)|a D zaDS=ScYJfFN58O=MREH2xGC1If3&SZoY7Q_&Yi|x2g`TK_4v J{}WGkMRJf%@8!sm)n6w>XFoCZQ{bbRCCJF}qzol?#znxDPbJIH6RDn=|x zB9ckb{4Hn>`(Vny zj2O8U^qY<{f^M NnKcgFa@mki&rCp$ziDAROD5 ~KG(}8*t;XJc#cHZ9 zea0{jJIFXt7t!uUdfvJ5uP}K|oQ_MnxD?QYiav<@oeMocxidfwkWS>2Uw_>Yr8~a^ zIaLf(d%4R}OBev9-9n3fm A>FAe2IuIsr;E?fz|)2g+^h^%tNAJS$4G0tVR2r}7R^jDEo{|&t?V6_}BGo-tp zn`9F9cQ#{pAaito4TR!AT!v0wUQ}LdB}C0Mlex;?nH@mpqj5tvqwvUn?AM{O&wQ zP*WRh+1olC3NpQ;xkfZi(tP^+i9|3}QwU?72$Rdt584cKuwd_(4BLe-ntA#SDasfm zQ-eMOg!Ql8YaBB>`2yv2ffmzfcCHlp{ 5fzU`7rcb0bmNxhu0IPG;{ z54(IZ*>Jk$XR^3_4|`su5l`8x8^f`A!Ci1;fJ*!^L2x{NNfO8E?!sVrME?$mv|FS= zU=PG}Za%;1dIMJY#rd7VPj i6?;)Gb^c}AS HjGT!8UL-P%$ElRf^8{mp$cGKRZysi6gSZ3<38Ly)!D4>?lTH3Q$ zmvCg&wlbT(8Hh27oxP!c45eyqY0!?dMdgqHO9OFtr}4hz9C)Mk#&kb3tJXVDX1|9b zU3Xr7`q;`5l%_{p|2-t-!Ase-?~d#YufQB0|mc2s{B838#~a@(bgQ{|S0vG;tT zGITZI0^BcKXQwk~kq@MmWAQd >UN6m zd`ohj*HPbSrobJOaz%O9Z$xnFmNSFR)*`gFOj rqZ5u0KBnzC;`}}d_)I?7bcEm4aQVa1y(!CD zTP!N)H(GYYW-ROUx8JedP7$%=$qrEVkC|Whyg!@M9p$L!j3liBTymNdDiC*Zu +VGECizz$+Bh z-G&g(2DyyxX8API2c-#1;#}3BM{|^YH%LmY$)iC<%R6+>wQ(W8>VqMMRZe2{do{nk z9M8>7Dw?@pjk)VjXZ#Mi3dhj~sH%BgxKl!fhv7X=LnLBAu*oX|F#A0M+wNshB3fz8 zI+t>&v|KHBvl)Pc$Ikw30;txYV~)su+R|{vt>S!mcLUvr&xves3Xc+pgn<(7Kz)BQ zr#rv4JGBt-6%+-KL6|nqLGl3)5qo`S`8p5lC(1{vFV2yl==M6*5U~;WAmG fTWfy>7#3rym^GX4subWdrtaeqZseR`{dS?L=Ys^n_GC%MS@M C$gB@t<56 z6$XGg`If}+T-Xf#HJsG-RekUlLTG6&^1{a0J0-q7X{wQPkK^m|_phVyikk85K%!xt z64S^)qtul{u7 AOL5aElPzB{a?D ~fH&kSS^^C!J&bC| M0<33t1|Dc=?*IwDHozvyk{8KilIr$8{kOC1+w<)3Jaf!1a4V!QvTYd1+ zvqd!c_Iyz6a|t8Y>)F|Ak@skh4D&2FPupQCxzvC{AUrib{lT6h|F%g}6TdH %Zr4I8;yZTRYWL@VJhZ|py{_*u%x|?eqC4_FG2jdhqFTjQYl?2S zivD(@b4z3nmgcK2+x&DhjHyRBPG-`k4Hzi$2xxDp*{<6QXNfz^YkQ7lgP)8%{E<(B zg_;;nb}&K1h7P+Ej_L+Rq0*|!X h-_kzi-5KHF_|IZr|SKF^%JPJ)k7U z|3KiRg0D)G)%FNyyQdgTSS_D3BStO*s>bq}thNPW$J%lJ*m5}b$2K&q@AxZI*HYpO zZU*N`yZmXu8aMkJ=v6mv-pRj*e6I#uH@a)Jgov#H2FIWTKiajNL+esz5qmOrUVsp> z?Lwu_z}NRb6;t3))$3Y)P2oun7?9dQIR$Ic;bOYm9_B2U+rxpt(0f)XpOH{<+_8zo zU;wSUnKiRCm|_Qa3JVt>t91+h<_TDE!H(UV8q_tP T z$PKt0_xcL96zgX2vX2bWW-c9=3+G{NM9mJTKqj?hNUzrMikCn$+0u!MWJX}{IfVU0 z(|o|UBV%qeP_C$=L6J+GFo2BJHa&j8a1q$h9G?|Wtij;oaKlt8V;^j!c%jzVi$w1% z)-WgEO)_jqztP{-aEZ^twdVFGL4s9bx8Om*iaRWNYAoJO&92`s95clW9y9R%(#W{Z z SVI1J`^URtwwFUUxl7Rng( zK~UHsd14K%(eg#{4T>j@a@%=Qhr=jYsQ5Guc1JTs(O~Cr43DiU9TLkY^L1`2qpVQQ zf7D%kJ)c4|g$h@$f Grc&F>0M4DQnklDmJOP7TG>{B~8e1!3BCZlHc}oNuHdd|kwg zpBU!5F-bLhedm%&X{gvbrM(=s6h6|h?ym2!X&9Z}ILoztgGr2#xNmc{qN$+G((_L( zNc75g!z1Uf8`*S>L_fK 5Y$<`1&t?R5{(C%ASVPKDUsrE&JalyleSXtp zCjKcdq6puH##!NH{2HdnL0>Emw4?v4_&(z4sqs7UR?~#tMyptZwv_z{PtKKD Rzy0rMZi%*tv9l2GSbqo`!~ wJmBl9f0coL z!4GqXcz)Q1pxE9oR@Y*@fUE6ykFkUb zF{6WTfH^l3cnONs@<<_Q26+3)OIKxufBTeEEbODz7)E8lX+5^EAC);?LNU(1ja`i* zoIyoEgrD?AU}%A=!Or@OA@EEmD_}j20Ssi2k2e`OSY8Kj4EYd{-Wl%*6%aBmINge^ zXKg;^{BhA=A(=15`G0<}j$!uG6t(SC*$PEjBf$(2pFuY}{@{pAc7C61HPuX&oPBBk zLg1d&APB SJFO4ASOu2{6t39HA{opd@Po7CGcKaKy6=71 z?%TxAC9uBB@fC;P6b(Heovbs*XLJu=rYSY4KYnhjQdKPcU~q&XM$VF4e$WW{Rc(Fd z&iE#UqB>xMHO|iWIyua5TRvK^-yfR9*i)R}g&YltnoZA%!JzNyi5c(~3?LYE2>AqS z9XP _p!)*$@^z3C#3xo<3N9Ll5;FAMa`HX)-b?WRY-B{6 oC!c-cdiN z #kXB5 z3Nf6U!!@qkUxBY!<~0JUV p=qjQ}ABdJ;Er!@ovE9zGtOVZV@fI zJEXBSZdugw1bAbO&iRWk{?DFU9ReP80FIPBJy)*>o1fkspFin8?_HJiU+*eB7MGvG zDqT1Iqc^|jRz~U2Jzl$RX8nI`X1byF&7~g0@aW6Ep*NqMlw7UmJL|`*x#jmdrEdUN zVKaYKTX?r`dXtan5>H?k#^`j@vaNc1zXSm{m`CNvFub>mH(?M zmtD@1-2S4$$KO0O__Ar{QN@`v^45HlwNYC7O6_~t0^L1d&CA|Im~QhrZm@@g`MO~D zs?W!!s2%SOy|~Ers)FHWy}B8zLskZDJeYjrBD;Leg`In@BO>@uk7!B$Va`uEyG1`; z_^tiqq~h--F~>uLR$bMqe6o7)rx)eFr<|&vEqDIPJm&VN-?ks_uWtgKQ1Yh*cvy$D z%{ym(;K2c#lM27Yz091UzkBW*51?1Mn6Gk+YKe#+j&58Vy*(^>?T3gDz!Vm0@A^LK zUbbu^uwVu*{Cpzgc J+<@KD~csf@CcV=w-7y?`y+^v1N>EWi^;G$*q9t~)~ zau|h`Uz5>2ZT^1ZbT`?F;`MfG&RqVi%&+~|y7u4B`M~5W*#0E)OuxmNy4JtT!t1;; zj{5_TCJB%=y8xV@TmEmqL++Y{O(io|#{7@J^&|{Y;A%yeugST!(SKV0P4&}e@23J! zlsPrYC-+`y0B1hayIbWEW$*2E+WTcDZrVDzTErsp5R2(Du0!WFOtP=}%+&s~#tC?E zLUQUd;2h?Z3w~uwAfwVA(>RU7_SQwHoZoBi`R>nqjh!#A3c7#wYRjv6x}0b4pUHn7 ze--?cbDHzg_v1%7->N8oaeLT)0)BwWdCpAGS_WX5mI^FMSHY_==j5r1yx_vlX%kOx zXq$; Y-z qN$oSxDmQYvQP>aIVSM}k*?aCdd2D0(118{UfefCmelF{r5}E**)Ilf! diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/RegisterLocationTrackingSpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/RegisterLocationTrackingSpec.java index f04cba17f0..f41afad328 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/RegisterLocationTrackingSpec.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/RegisterLocationTrackingSpec.java @@ -84,8 +84,12 @@ public interface RegisterLocationTrackingSpec extends LocationTrackingSpec, Loca if (value == null) { return null; } - // TODO: Action to select the address space - // Could use code unit, but that can't specify space, yet, either.... + /** + * NOTE: I don't think the user needs a way to select the address space. For PC and SP, the + * tracker provides the best default, i.e., the default (code) space and the compiler's + * physical stack space. For watches, I believe the sleigh syntax allows the user to pick, + * but I can't recall testing that. + */ return platform.mapGuestToHost(computeDefaultAddressSpace(coordinates) .getAddress(value.getUnsignedValue().longValue(), true)); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/SPLocationTrackingSpec.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/SPLocationTrackingSpec.java index 7e7722bdfe..e9da700410 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/SPLocationTrackingSpec.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/action/SPLocationTrackingSpec.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -59,7 +59,7 @@ public enum SPLocationTrackingSpec implements RegisterLocationTrackingSpec { @Override public AddressSpace computeDefaultAddressSpace(DebuggerCoordinates coordinates) { - return coordinates.getTrace().getBaseLanguage().getDefaultDataSpace(); + return coordinates.getPlatform().getCompilerSpec().getStackBaseSpace(); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerSnapshotTablePanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerSnapshotTablePanel.java index bf51354785..e9cd96baa8 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerSnapshotTablePanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/DebuggerSnapshotTablePanel.java @@ -31,6 +31,7 @@ import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.docking.settings.Settings; import ghidra.framework.model.DomainObjectEvent; import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.address.Address; import ghidra.trace.model.Trace; import ghidra.trace.model.TraceDomainObjectListener; import ghidra.trace.model.target.TraceObjectValue; @@ -50,8 +51,11 @@ public class DebuggerSnapshotTablePanel extends JPanel { implements EnumeratedTableColumn { SNAP("Snap", Long.class, SnapshotRow::getSnap, false), TIME("Time", TraceSchedule.class, SnapshotRow::getTime, true), - TIMESTAMP("Timestamp", Date.class, SnapshotRow::getTimeStamp, true), EVENT_THREAD("Event Thread", String.class, SnapshotRow::getEventThreadName, true), + PC("PC", Address.class, SnapshotRow::getProgramCounter, true), + MODULE("Module", String.class, SnapshotRow::getModuleName, true), + FUNCTION("Function", ghidra.program.model.listing.Function.class, SnapshotRow::getFunction, true), + TIMESTAMP("Timestamp", Date.class, SnapshotRow::getTimeStamp, false), SCHEDULE("Schedule", TraceSchedule.class, SnapshotRow::getSchedule, false), DESCRIPTION("Description", String.class, SnapshotRow::getDescription, // SnapshotRow::setDescription, true); @@ -140,7 +144,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { if (snapshot.getKey() < 0 && hideScratch) { return; } - SnapshotRow row = new SnapshotRow(snapshot); + SnapshotRow row = new SnapshotRow(snapshot, tool); snapshotTableModel.add(row); } @@ -175,7 +179,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { @Override protected String formatNumber(Number value, Settings settings) { return switch (value) { - case null -> " "; + case null -> ""; // SNAP is the only column with Long type case Long snap -> getTimeRadix().format(snap); default -> super.formatNumber(value, settings); @@ -185,7 +189,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { @Override protected String getText(Object value) { return switch (value) { - case null -> " "; + case null -> ""; case Date date -> DateUtils.formatDateTimestamp(date); case TraceSchedule schedule -> schedule.toString(getTimeRadix()); default -> value.toString(); @@ -195,7 +199,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { @Override public String getFilterString(Object t, Settings settings) { return switch (t) { - case null -> " "; + case null -> ""; // SNAP is the only column with Long type case Long snap -> getTimeRadix().format(snap); case Number n -> formatNumber(n, settings); @@ -221,6 +225,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { } }; + protected final PluginTool tool; protected final SnapshotTableModel snapshotTableModel; protected final GTable snapshotTable; protected final GhidraTableFilterPanel snapshotFilterPanel; @@ -233,6 +238,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { public DebuggerSnapshotTablePanel(PluginTool tool) { super(new BorderLayout()); + this.tool = tool; snapshotTableModel = new SnapshotTableModel(tool); snapshotTable = new GTable(snapshotTableModel); snapshotTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -243,22 +249,31 @@ public class DebuggerSnapshotTablePanel extends JPanel { TableColumnModel columnModel = snapshotTable.getColumnModel(); TableColumn snapCol = columnModel.getColumn(SnapshotTableColumns.SNAP.ordinal()); - snapCol.setPreferredWidth(40); + snapCol.setPreferredWidth(20); snapCol.setCellRenderer(styleCurrentRenderer); TableColumn timeCol = columnModel.getColumn(SnapshotTableColumns.TIME.ordinal()); - timeCol.setPreferredWidth(40); + timeCol.setPreferredWidth(20); timeCol.setCellRenderer(styleCurrentRenderer); + TableColumn etCol = columnModel.getColumn(SnapshotTableColumns.EVENT_THREAD.ordinal()); + etCol.setPreferredWidth(20); + etCol.setCellRenderer(styleCurrentRenderer); + TableColumn pcCol = columnModel.getColumn(SnapshotTableColumns.PC.ordinal()); + pcCol.setPreferredWidth(40); + pcCol.setCellRenderer(styleCurrentRenderer); + TableColumn moduleCol = columnModel.getColumn(SnapshotTableColumns.MODULE.ordinal()); + moduleCol.setPreferredWidth(40); + moduleCol.setCellRenderer(styleCurrentRenderer); + TableColumn functionCol = columnModel.getColumn(SnapshotTableColumns.FUNCTION.ordinal()); + functionCol.setPreferredWidth(40); + functionCol.setCellRenderer(styleCurrentRenderer); TableColumn timeStampCol = columnModel.getColumn(SnapshotTableColumns.TIMESTAMP.ordinal()); timeStampCol.setPreferredWidth(200); timeStampCol.setCellRenderer(styleCurrentRenderer); - TableColumn etCol = columnModel.getColumn(SnapshotTableColumns.EVENT_THREAD.ordinal()); - etCol.setPreferredWidth(40); - etCol.setCellRenderer(styleCurrentRenderer); TableColumn schdCol = columnModel.getColumn(SnapshotTableColumns.SCHEDULE.ordinal()); schdCol.setPreferredWidth(60); schdCol.setCellRenderer(styleCurrentRenderer); TableColumn descCol = columnModel.getColumn(SnapshotTableColumns.DESCRIPTION.ordinal()); - descCol.setPreferredWidth(200); + descCol.setPreferredWidth(20); descCol.setCellRenderer(styleCurrentRenderer); } @@ -319,7 +334,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { for (TraceSnapshot snapshot : hideScratch ? manager.getSnapshots(0, true, Long.MAX_VALUE, true) : manager.getAllSnapshots()) { - SnapshotRow row = new SnapshotRow(snapshot); + SnapshotRow row = new SnapshotRow(snapshot, tool); toAdd.add(row); if (current != DebuggerCoordinates.NOWHERE && snapshot.getKey() == current.getViewSnap()) { @@ -340,7 +355,7 @@ public class DebuggerSnapshotTablePanel extends JPanel { Collection extends TraceSnapshot> sratch = manager.getSnapshots(Long.MIN_VALUE, true, 0, false); snapshotTableModel.addAll(sratch.stream() - .map(s -> new SnapshotRow(s)) + .map(s -> new SnapshotRow(s, tool)) .collect(Collectors.toList())); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/SnapshotRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/SnapshotRow.java index aac8473150..87c389db13 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/SnapshotRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/time/SnapshotRow.java @@ -18,17 +18,32 @@ package ghidra.app.plugin.core.debug.gui.time; import java.util.Date; import db.Transaction; +import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.Register; +import ghidra.program.model.lang.RegisterValue; +import ghidra.program.model.listing.Function; import ghidra.trace.model.Trace; +import ghidra.trace.model.guest.TraceGuestPlatform; +import ghidra.trace.model.guest.TracePlatform; +import ghidra.trace.model.memory.*; +import ghidra.trace.model.stack.TraceStack; +import ghidra.trace.model.stack.TraceStackFrame; import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.schedule.TraceSchedule; public class SnapshotRow { private final TraceSnapshot snapshot; + private final ServiceProvider serviceProvider; + private final Trace trace; - public SnapshotRow(TraceSnapshot snapshot) { + public SnapshotRow(TraceSnapshot snapshot, ServiceProvider serviceProvider) { this.snapshot = snapshot; + this.serviceProvider = serviceProvider; this.trace = snapshot.getTrace(); } @@ -52,9 +67,127 @@ public class SnapshotRow { return new Date(snapshot.getRealTime()); } - public String getEventThreadName() { + private Address getProgramCounterByStack() { TraceThread thread = snapshot.getEventThread(); - return thread == null ? "" : thread.getName(snapshot.getKey()); + if (thread == null) { + return null; + } + long snap = getTime().getSnap(); + TraceStack stack; + try { + stack = trace.getStackManager().getLatestStack(thread, snap); + } + catch (IllegalStateException e) { + // Schema does not specify a stack + return null; + } + if (stack == null) { + return null; + } + TraceStackFrame frame = stack.getFrame(snap, 0, false); + if (frame == null) { + return null; + } + return frame.getProgramCounter(snap); + } + + private Address getProgramCounterByRegister() { + TraceThread thread = getEventThread(); + if (thread == null) { + return null; + } + long viewSnap = snapshot.getKey(); + long snap = getTime().getSnap(); + /** + * LATER: Some notion of an event platform? Or perhaps the thread has some attribute to + * indicate which platform is active? + * + * I could use the tool's "current" platform, but that may produce odd behavior when + * changing platforms. Each would have the most recent PC for the selected platform, which + * is totally irrelevant. For now, seek out the platform with the most recent update to its + * PC for the event thread. While this should be perfectly accurate, it's a bit expensive. + */ + record MostRecentValue(TracePlatform platform, long snap, RegisterValue value) { + static MostRecentValue choose(MostRecentValue a, MostRecentValue b) { + if (a == null) { + return b; + } + if (b == null) { + return a; + } + // Prefer negative ("view") snaps to positive. Of that, pick most recent. + if (Long.compareUnsigned(a.snap, b.snap) > 0) { + return a; + } + return b; + } + + static MostRecentValue get(TracePlatform platform, TraceThread thread, long viewSnap, + long snap) { + Register reg = platform.getLanguage().getProgramCounter(); + TraceMemoryManager mm = thread.getTrace().getMemoryManager(); + TraceMemorySpace regs = reg.getAddressSpace().isRegisterSpace() + ? mm.getMemoryRegisterSpace(thread, false) + : mm.getMemorySpace(reg.getAddressSpace(), false); + if (regs == null) { + return null; + } + if (regs.getState(platform, viewSnap, reg) == TraceMemoryState.KNOWN) { + RegisterValue value = regs.getValue(platform, viewSnap, reg); + return value == null ? null : new MostRecentValue(platform, viewSnap, value); + } + RegisterValue value = regs.getValue(platform, snap, reg); + return value == null ? null : new MostRecentValue(platform, snap, value); + } + + Address mapToHost() { + AddressSpace codeSpace = platform.getAddressFactory().getDefaultAddressSpace(); + return platform.mapGuestToHost( + codeSpace.getAddress(value.getUnsignedValue().longValue(), true)); + } + } + MostRecentValue choice = MostRecentValue.get(trace.getPlatformManager().getHostPlatform(), + thread, viewSnap, snap); + for (TraceGuestPlatform guest : trace.getPlatformManager().getGuestPlatforms()) { + choice = + MostRecentValue.choose(choice, MostRecentValue.get(guest, thread, viewSnap, snap)); + } + return choice == null ? null : choice.mapToHost(); + } + + public Address getProgramCounter() { + Address byStack = getProgramCounterByStack(); + return byStack != null ? byStack : getProgramCounterByRegister(); + } + + public Function getFunction() { + Address pc = getProgramCounter(); + if (pc == null) { + return null; + } + return DebuggerStaticMappingUtils.getFunction(pc, trace, getTime().getSnap(), + serviceProvider); + } + + public String getModuleName() { + Address pc = getProgramCounter(); + if (pc == null) { + return null; + } + return DebuggerStaticMappingUtils.getModuleName(pc, trace, getTime().getSnap()); + } + + private TraceThread getEventThread() { + TraceThread thread = snapshot.getEventThread(); + if (thread != null) { + return thread; + } + return getTime().getEventThread(trace); + } + + public String getEventThreadName() { + TraceThread thread = getEventThread(); + return thread == null ? "" : thread.getName(getTime().getSnap()); } public TraceSchedule getSchedule() { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java index 8cbf630e09..c69a2a72bf 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/modules/DebuggerStaticMappingUtils.java @@ -316,6 +316,11 @@ public enum DebuggerStaticMappingUtils { public static Function getFunction(Address pc, DebuggerCoordinates coordinates, ServiceProvider serviceProvider) { + return getFunction(pc, coordinates.getTrace(), coordinates.getSnap(), serviceProvider); + } + + public static Function getFunction(Address pc, Trace trace, long snap, + ServiceProvider serviceProvider) { if (pc == null) { return null; } @@ -324,8 +329,7 @@ public enum DebuggerStaticMappingUtils { if (mappingService == null) { return null; } - TraceLocation dloc = new DefaultTraceLocation(coordinates.getTrace(), - null, Lifespan.at(coordinates.getSnap()), pc); + TraceLocation dloc = new DefaultTraceLocation(trace, null, Lifespan.at(snap), pc); ProgramLocation sloc = mappingService.getOpenMappedLocation(dloc); if (sloc == null) { return null; @@ -346,14 +350,13 @@ public enum DebuggerStaticMappingUtils { } public static String getModuleName(Address pc, DebuggerCoordinates coordinates) { - if (pc == null) { + return getModuleName(pc, coordinates.getTrace(), coordinates.getSnap()); + } + + public static String getModuleName(Address pc, Trace trace, long snap) { + if (pc == null || trace == null) { return null; } - Trace trace = coordinates.getTrace(); - if (trace == null) { - return null; - } - long snap = coordinates.getSnap(); for (TraceModule module : trace.getModuleManager().getModulesAt(snap, pc)) { // Just take the first return computeModuleShortName(module.getName(snap)); diff --git a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimePluginScreenShots.java b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimePluginScreenShots.java index 77372ae33e..a54e799021 100644 --- a/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimePluginScreenShots.java +++ b/Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/gui/time/DebuggerTimePluginScreenShots.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,28 +15,84 @@ */ package ghidra.app.plugin.core.debug.gui.time; +import java.io.IOException; +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + import org.junit.*; import db.Transaction; +import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils; +import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin; import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin; -import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin; +import ghidra.app.services.*; +import ghidra.framework.model.DomainFolder; +import ghidra.framework.model.DomainObject; +import ghidra.program.model.lang.Register; +import ghidra.program.model.lang.RegisterValue; +import ghidra.program.model.listing.Program; +import ghidra.program.model.symbol.SourceType; +import ghidra.program.util.ProgramLocation; import ghidra.test.ToyProgramBuilder; import ghidra.trace.database.ToyDBTraceBuilder; +import ghidra.trace.model.DefaultTraceLocation; +import ghidra.trace.model.Lifespan; +import ghidra.trace.model.memory.TraceMemorySpace; +import ghidra.trace.model.target.TraceObject.ConflictResolution; +import ghidra.trace.model.target.path.KeyPath; import ghidra.trace.model.thread.TraceThread; import ghidra.trace.model.time.TraceSnapshot; import ghidra.trace.model.time.schedule.TraceSchedule; +import ghidra.util.InvalidNameException; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.ConsoleTaskMonitor; +import ghidra.util.task.TaskMonitor; import help.screenshot.GhidraScreenShotGenerator; public class DebuggerTimePluginScreenShots extends GhidraScreenShotGenerator { + private static final TaskMonitor MONITOR = new ConsoleTaskMonitor(); + + ProgramManager programManager; DebuggerTraceManagerService traceManager; + DebuggerStaticMappingService mappingService; + DebuggerTimePlugin timePlugin; DebuggerTimeProvider timeProvider; + ToyDBTraceBuilder tb; + Program progHw; + Program progLibc; + + protected void intoProject(DomainObject obj) { + waitForDomainObject(obj); + DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder(); + waitForCondition(() -> { + try { + rootFolder.createFile(obj.getName(), obj, MONITOR); + return true; + } + catch (InvalidNameException | CancelledException e) { + throw new AssertionError(e); + } + catch (IOException e) { + // Usually "object is busy". Try again. + return false; + } + }); + } + + public static void waitForDomainObject(DomainObject object) { + object.flushEvents(); + waitForSwing(); + } @Before public void setUpMine() throws Throwable { + programManager = addPlugin(tool, ProgramManagerPlugin.class); traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class); + mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class); timePlugin = addPlugin(tool, DebuggerTimePlugin.class); timeProvider = waitForComponentProvider(DebuggerTimeProvider.class); @@ -46,42 +102,105 @@ public class DebuggerTimePluginScreenShots extends GhidraScreenShotGenerator { @After public void tearDownMine() { tb.close(); + + if (progHw != null) { + progHw.release(this); + progHw = null; + } + if (progLibc != null) { + progLibc.release(this); + progLibc = null; + } } @Test public void testCaptureDebuggerTimePlugin() throws Throwable { + progHw = createDefaultProgram("helloworld", ToyProgramBuilder._X64, this); + progLibc = createDefaultProgram("libc", ToyProgramBuilder._X64, this); + long fakeClock = (long) Integer.MAX_VALUE * 1000; TraceSnapshot snap; + + try (Transaction tx = progHw.openTransaction("Populate main")) { + progHw.getMemory() + .createInitializedBlock(".text", tb.addr(0x00400000), 0x2000, (byte) 0, MONITOR, + false); + progHw.getFunctionManager() + .createFunction("main", tb.addr(0x00401234), + tb.set(tb.range(0x00401234, 0x00401300)), SourceType.IMPORTED); + } + try (Transaction tx = progLibc.openTransaction("Populate puts")) { + progLibc.getMemory() + .createInitializedBlock(".text", tb.addr(0x00400000), 0x2000, (byte) 0, MONITOR, + false); + progLibc.getFunctionManager() + .createFunction("puts", tb.addr(0x00400110), + tb.set(tb.range(0x00400110, 0x00400120)), SourceType.IMPORTED); + } + + intoProject(progHw); + intoProject(progLibc); + intoProject(tb.trace); + programManager.openProgram(progLibc); + programManager.openProgram(progHw); + traceManager.openTrace(tb.trace); + mappingService.changesSettled().get(1, TimeUnit.SECONDS); + try (Transaction tx = tb.startTransaction()) { - snap = tb.trace.getTimeManager().createSnapshot("Trace started"); - snap.setRealTime(fakeClock); + tb.trace.getObjectManager().createRootObject(ProgramEmulationUtils.EMU_SESSION_SCHEMA); - TraceThread thread = tb.getOrAddThread("[1]", snap.getKey()); + tb.trace.getModuleManager() + .addLoadedModule("Modules[helloword]", "helloworld", + tb.range(0x00400000, 0x00402000), 0); + tb.trace.getModuleManager() + .addLoadedModule("Modules[libc]", "libc", + tb.range(0x7fff0000, 0x7fff2000), 0); - snap = tb.trace.getTimeManager().createSnapshot("Thread STOPPED"); + mappingService.addMapping( + new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), tb.addr(0x00400000)), + new ProgramLocation(progHw, tb.addr(0x00400000)), 0x2000, false); + mappingService.addMapping( + new DefaultTraceLocation(tb.trace, null, Lifespan.nowOn(0), tb.addr(0x7fff0000)), + new ProgramLocation(progLibc, tb.addr(0x00400000)), 0x2000, false); + + TraceThread thread = tb.getOrAddThread("Threads[1]", 0); + tb.trace.getObjectManager() + .createObject(KeyPath.parse("Threads[1].Registers")) + .insert(Lifespan.nowOn(0), ConflictResolution.DENY); + thread.setName(0, "1 main"); + TraceMemorySpace regs = + tb.trace.getMemoryManager().getMemoryRegisterSpace(thread, true); + Register pc = tb.host.getLanguage().getProgramCounter(); + + snap = tb.trace.getTimeManager().createSnapshot("STOP"); snap.setEventThread(thread); snap.setRealTime(fakeClock); fakeClock += 1000; + regs.setValue(snap.getKey(), new RegisterValue(pc, BigInteger.valueOf(0x00401234))); - snap = tb.trace.getTimeManager().createSnapshot("Thread BREAKPOINT_HIT"); + snap = tb.trace.getTimeManager().createSnapshot("BREAK"); snap.setEventThread(thread); snap.setRealTime(fakeClock); fakeClock += 2300; + regs.setValue(snap.getKey(), new RegisterValue(pc, BigInteger.valueOf(0x7fff0110))); - snap = tb.trace.getTimeManager().createSnapshot("Thread STEP_COMPLETED"); + snap = tb.trace.getTimeManager().createSnapshot("STEP"); snap.setEventThread(thread); snap.setRealTime(fakeClock); snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1")); fakeClock += 444; + regs.setValue(snap.getKey(), new RegisterValue(pc, BigInteger.valueOf(0x7fff0113))); - snap = tb.trace.getTimeManager().createSnapshot("Thread STEP_COMPLETED"); + snap = tb.trace.getTimeManager().createSnapshot("STEP"); snap.setEventThread(thread); snap.setRealTime(fakeClock); snap.setSchedule(TraceSchedule.parse(snap.getKey() - 1 + ":1")); fakeClock += 100; + regs.setValue(snap.getKey(), new RegisterValue(pc, BigInteger.valueOf(0x7fff0115))); } - traceManager.openTrace(tb.trace); + mappingService.changesSettled().get(1, TimeUnit.SECONDS); + traceManager.activateTrace(tb.trace); traceManager.activateSnap(snap.getKey()); diff --git a/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html b/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html index ec03eacf85..9a149b9e11 100644 --- a/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html +++ b/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html @@ -234,11 +234,16 @@ snapshot is at the bottom. The columns are: windows that indicate life spans refer to these numbers. If emulating (covered later in this course), this column may display the schedule.
CTRL-SHIFT-N to
diff --git a/GhidraDocs/GhidraClass/Debugger/A5-Navigation.md b/GhidraDocs/GhidraClass/Debugger/A5-Navigation.md
index 96b75851ca..b842ddb110 100644
--- a/GhidraDocs/GhidraClass/Debugger/A5-Navigation.md
+++ b/GhidraDocs/GhidraClass/Debugger/A5-Navigation.md
@@ -125,9 +125,11 @@ The columns are:
* The **Time** column numbers each snapshot.
Other windows that indicate life spans refer to these numbers.
If emulating (covered later in this course), this column may display the schedule.
-* The **Timestamp** column gives the time when the snapshot was created, i.e., the time when the event occurred.
* The **Event Thread** column indicates which thread caused the target to break.
This only applies to snapshots that were created because of an event, which is most.
+* The **PC** column gives the address of the next instruction.
+* The **Function** column gives the name of the function containing the PC mapped to its static program database, if available.
+* The **Module** column gives the name of the module containing the PC.
* The **Description** column describes the event that generated the snapshot.
This can be edited in the table, or by pressing **`CTRL`-`SHIFT`-`N`** to mark interesting snapshots.
diff --git a/GhidraDocs/GhidraClass/Debugger/images/Navigation_DialogCompareTimes.png b/GhidraDocs/GhidraClass/Debugger/images/Navigation_DialogCompareTimes.png
index 05d09d046ece3bb8ce5c582ce24baac7b54789b6..1fa3931b45769cfd2fe46b7c112c48cbf3c348b7 100644
GIT binary patch
literal 15380
zcmeIZhgVbE+C7Y*B6~7#BPHmP^3)eEMbu{D~ zZT~_-YGv+?Xt(5j>?9+% z^}}l0jd&&aeOVs0G3_jI>54ba4M%bn4^}pt>sf|nwbbU*GR8g|;w zt@gxrYiP&@&UW;}J(2zEyY?H8d|Zp(C`RjDqClM;e&{9T3ku12cvm`6ob|%|bKPD2 zhcn|e4|a((cBkxYYrm_v2n|Tn4bT7M>0p;N6l@|CHZ>|=<1SZQ_U+*H%ZFbcQ}gzw zzun~D!4-ec+m$Lo# @tN%Ac6-(ws2RY&hviP@!Oo0#v$!4V QU`fvi}I z8u8dUrNBTJ`t9{)#jceB#qzSGL$$3n??G@detat{-WUaT)m*84*-}!wUYf)wSkSYb zd-exmssNsj|L2Fg7Af=>YW@E6OE&`l0x$b1$d(Wj(|VNNlqSy=CMJ3^6Ks-&>1^^j z_B~9mU3oIn(tc`%9l&}wvNJKg2!QNmI$HzT8b$N_EKeDvt4ArlJQyWnQfiR%aFNnc zCM$jCl;Jrk4#FNL|7Ul&(1%Z7pX^F+F*NrpaT%_5pYGe =bFD9{C!n{ zLP#W9$@^BZ$u>Km+|M$XVfc7+)D;8!rOD3n`y~6WG_@P{J)wtPDi^+=5PmqhQj6x` z;DLMw_r4p%H9Lr1q!BCJH+Qx-`H^d*mi2E8@=d*4W5qAd=KgdoL(Cre>tTXV-zcBX zubAja#@Ti7IT+l#cdyE4xi8nK$fhM4&F7;krEW^}rX {}GcnbSo z%!aFc1-Hw8{OwOL79UceP-`tI$`JaLZ|9FdVKo)An7vVUcg!#k^ZKWHbKL!=`|`$H zV({+XmrSwn^!%2g!2&yx_iym*es&ytnV5pugC4`49IKz5AFWMB?RYo5 GQw07c1ibxzq2Rp&K26vA}(bb82Q Jgc>VC|^ob$L`V@uxd-!XfcA$Ws3~CZ^j7Ry=+AW(YdD35=VFL!oSK zCs;V-etfzwP-Xw)!|mm%-Ux#}Pqf`lkE`!^q$T9#&BAq*oI5|>ZF_&Cd)9Ar#p%a~ zZ}$|bk2f+iGFIk>QQ%4GAG-*=Xs9fRqt~u)Yxf=E8g$2-8Jfag%H tGD6;`eN)JQyxuvRIvk*IjP&E8 zYZ}vtRPux#k29h0f~4ePT_ESmo|lZ1O}0J`2KZ^hj%?q)hhy~ >Y_TfnDkl zZ3y8n3%Mv)IC>CwY4tI`KF=LgXy@E*em2+>@FbA&hdlSZMY>QN#U=t_k}M(r+>UF! zmATOhe(}WM$4ajHYeJ+e8v$>T92=^yl}#cg51kE>rabt(W6~B@n12_yMQ>@~M{VUw z843GoqU^M<8Wq;jX6>9Tr76!<1Yxh@xJlQVUE9GtJUbYE@SB*zH~cGWvmo+&1zOf2 z{Enmz(PYn?;ud47)rPFA<^0IEQAbNQiTRW22^ROb(3eGR4U$Grpy95anL5dq$nA~g zLvYNVvtnTmfBKCs7GZcrN0-=Y%DLnhTO}Pb2Jk+1VI^G+OB`{WG_9)_g&yWU>il#+ zHE1iX(083)6mtDapk(Xou^&B)S1^zS2#hp`+TEPnN|W%WTUW69S2mi8`W=O`2u#h` zngzZ3<5V~mWvia!={j7b7WB_Ue*1GDc*stc-HmCCM&u=N%en*GI;RB ygERvPh0IVbF(wxilpRYLL~Wa%xC>UY-~6ludabI;I~7;_{RgdHW( zGpS>DR~PbZ ^Yg;E7KlWbsvqRS S(Rfc~b|#2ITdROwwV8YSaCl~BcZYv{NjdmHIn0P8r(B;QmT zyVL02VJwWeq@U5+7$)Gy$1QGWS6j{%r^25F8K`c$P3fm4i_?+cBK#A}xAq>*z@BhOF2srB`_`PQ>4I>I#B) zhebz6$Hif-8rb7VeDXe%D_!XtxP|X$5UaFpQji#}dXvT&7}+h7yMM%Uz^SYWVJkBT z;1S?dMDFY`4rOnj?d<97;MF#BA;$FldiFG&>DZ7RhBZ{`B`5mSsZ#(SOp0uS dWkcZ&S=8>#>F@sB;@yPHx8X`jqSmJEED?9r{LEI%+l7eN{3u7zTtr5qcymH{ zWvW*Lj38sNIZD`ld$B_iV~Q<&x;qi?;%4?B=b;n0e!1vCw3s;*Kd#^aU;)g9lzi3c zHj75yH{N%>_qtVoT%Ti~Arz@NKXQ{li-{A_{cy{}qcmS*d<$F-A@5IRR2hZx$x* zJ~8W4*xg#Bn|^uHdA|GP{CTcWELSkE> N5hB54k@(&)*3$-vl Dq5mNgiT6kil26q6&ii(uOu4g8K+OfH%ELQcAv cx0D%7lQ*J53#(7`CW9E)nCNTq8*$*f_4I z-VQo9I!23{$*86hMk90-o>t3Nf(PGRqFT8>6DwCC-zeg|s5ugoeoNuFHV^yh>z|-i zJ|KU&qddPn?Y@Ym#t46L{=@62WMX2jLzs1K@?4ef8}`7(oi!b^$T99HJ@IX#^yr{q zBV#Uma}3Qr2sRyZi*MI$vaY^2o&Q&qKAriIQv0*ON{c@(w>?pI`WKcwDzGfDtS4_h z+iCB1TUkdL$=LUN0&{%g%7af2^Nb~JTYt>bbG?b~c(qjWbb_?&pV!~>Huosf8wFxE z)#~MHOfZwC#Shm_=Ykhn#Bi#ET+0!w0VvM{WX{pL(dR6zKC3jdQb!}~5^lccP@l0{ z7@!UuC$D{_z>d0l+#CdvvE?Q|5=Ff1rTyvJitYX#5WY7Ye}4X){9?G!rls|^j~OKb z78_P?ac8hr`yg#~|1IRi+uDTVTgJ!dn`y@-gp~3R+KqIYQ#kEn>rzD45G-eLY(ghU z=b9bDWQES3X#bjuYghRvX$8S;_mIU$)0E`yc#FKT(B6A=+g;&zY9gIPt6gc1+ZWpK zBI4k}k?q|arzOPakjv~J6&oj4KZLx16obT@02gL2b0mbv#d(wJ*f#x@!nCZFx0QeV zAL-HdS86s$GFuvEsTKNPQ?sqkNq^stYR$7^=RtBm3yuSl;J?O2r8+=s)>#=O-~*%> zWx5ZL0d5HUiT0JT5XFA2^)sBu4%>>UYd*dnD=!ttNxEj5aEt4r;OSOqWbD@`*AUKi z7JDJjuWG7ZkFB~S{=D%HZqT1J9U^Vne}i?^`mbew)D_MIRm6TX^jp`0dBKz4rnL zt^(`=U)iiJls gsQZ#~qq(oC~~Y zWGhAvJYJ}rAlS0^;zipIkx|mMQhpIOrS;)+%CBaIal=q a9ftfX~^YXLlqy(7c>0eq3f)e+l 6(-UTGk!L#}HLQ<`1kg`C&KqO>vH$IS4DF0DG4ee-PsF1E{!!bDJAv$zB zmHXENVFs55T|49f1MdlnmvV{r_uW~ifGQy=Dyqb5fpk&peFEX?Y8_(5TFD-tFOOJp z?3QroyZSV6sqO$l(Had2Y}otV6z~B9_f=rB$3?8g31ehmPDr-3dd}0;hq}BAM|I zUL6)S^(jV?&1(x|yA;$;rf!N79*UR+fC}{ *}LxH*OfvR^7|GGj$XJ4gz9- z1@IYszS+2wY>^m>XJ*L}6p{M~ZSfo#;6%z4=0n%tl6*vGYK5Ki&xpVC9f85yfr6 zpDxJ22~E=&oapNE%R}7JVYarmQ}=3Tnn_7&t4Oy^kY`$=goUL1+IJ{t$uG!)lzGp& zAwPmm47Hq0`fO3#nq7Bvju64-J)KuZe)_Yx@#*}^fJ=O!406k>BS~H(MpUHi5a1z1 zB-mB!pBro4z_XGipKMg Yp47@9rs*s$YoW9-wz~S5v;Sh-*RtvhE{hlP5OSmLzpW(tpijx zmy<~id kPJkqydUr3Jnnc9A1!BPo+HQe;Qh2K8{GBC?Pu^<4$t;st}LgRkq!U%w- zO(L;xAJ7L(+nH&DP4W@Y&8LLI@qDw2gv1W5Mz$uk_R%?z`e{mTW(w!v{jmmt0z*YY zoaec(VgpP|9pkPd=}kg 6}Y9ObiE2G)VI~}wW@aHLYQHjSa@9 UzXzXjH%npz(e-KieWhA*vKF7X4KxkTJ}mG`?zx_6 tWh+Z0P*9Y$d)L*(kM(tKQQKjQs!Muq>@l9INnwnS}=jv+#^$_wz5MxjMb+zSvl z-JJ9?+_n}?ARVyO2v8!$d7Sec9`;=f&Tk1Wk~=h2{vcsC1Gl-i=n6E`jj^Sd8~UyV zKjhbETRU1Ym7O!laULqW;KN<%!6!W0n&jSkURHLq=8#wqW8i?+TQ$5R7FeO7J(Ux6 z-Xd`A+_(s^j?$DnhPm~R+ANeBEWUa;+`kt~0*q{rom6106muK|_HmF|7V%1e%x2A- z)(IHPDM?#Zn~f#vgsccgpv=RPtIEUK%jt=Tdj0z1n GT${xt^?u^>#%? zMcymB!(n9S?KY|5$lYGl9SJ)nP#LrZ|M|dL{BZ^T)THyOO!}B0ALo-`@yDr%k-m?v z%)!rOWQX@*c7DaW`GX|(@jG2$6bOJ2bWM=`V+`_)-5I3)ujN~W>jgg1w!DE><3|I2 zz(McL2buX6Ij%}4SwSyw^4I!QW^C4SC{~U~n?&xm!~>Ajlk(xk;#HNAy_Axal1cZ_ zzjieS*0kO5`l!H6-`*p01W8VMt7J!`K;e2&)zj7T%;@HgzS&zpl6JOXaR1eUcD>_rO!z>O+S%x8M>O>ByRP6W)LeWEUckD zBTM^ION%@M@&uIAvY+*jlaH%mS?0K%ug_WBD@^I{uRpb{#)@xG_hd=BP5uB}&v%vR z2`?}2xb*jK6A~eR&8r9eIWs*S2xVpMb4|G&E{%VqQXF@b)C1ZWVr7Q9Ws`*;AQswa zP_ODdJ%*mRFENtf6@357#2=1>UkcVmuMN4zF2TGumYxR2oxWjzT-5Z7$87%`ZnwH9 ze*I`
@VlQ5-t1yK$*_j%|C1RiFPVfI=>^Ap}Kr-OC1fV$W ztcoyy3c$6NnG+x>Iupw$I3QTzp(4tSa~Wo$j~G3J%ZgdCic-9w)-)+|2FWufp2o&` zF?I(&ocR9zmCWg0L-TD=saE{Y;m*v=02M1n*igIX)
Wo8VeD2 zg+XG~Uu@gn`)w={itIv+rk6cB5~THIxc{P;hqy(1g7;AB9Nu2uF^_(jZ)&8i)TF(r z(Fsx;t--=Hm|wA4Uw1+XH+q1wx7qL?))LlO#4S-q5)5guaJ)cgixmF%->C3he11pa zIegzgEKx;5zx?BpKpvxok V*sS&Tj$| zyrcGpVfG&qd~LlxcoZEdY}cOh`)3Ge`r`3%N&&jo0a@7}9Ljs_&R!T}g<1K$MvmcF z`F(Y{X8G3jnq6j2SV$tCb zeyzHeX}%_3qPj_mK0tZ~)GnZ5F5|Y-B&-{cRH!I$MXV0=t7)#B(sMMgpB7xO$M*w5 zQe>w?3G-TBwdaWN4!pF=DOBJm=KuNyYUAaweo!&WIq)pILtn16Ma^Eb7 ~5f=XoyuUfqLEf$A{ J zM5F~Q_v?tN!+wZB=W^Yd8$bRro1K%tL7YPh?QZrvU;r%S0vWaNQf9CwhH$0TIpf&; z(xOZ@Zg+bQZgmb(0ZL|-cRX2VTpz_+Ey Fe1t=8b0}`<=0if{ zS7Nc&8VDbs4IpFXawq%Jo8n!njgsKeA|@U{mKg$NWqFlxiM@V?W>~CIwJ$c|l2d^xn|= z0p|)Q0PpZ5KnFRw&D_FrxAC~0bZ$^I@;;T)XY< hWHa9Ew zL#}V3b{dTZ6sL3OxtR~beU|?=>S>NIq0Olw3fTi4T6 r=Jh9!*t-8 ztB1qj$<|<4f%zEY5_RWP&58uk3=;stifbb*X6D6D!Ujv@4PGTE)32pW(-W445OdV> zLEn0E<6%rhU5+0}+ITb1#GR>jZ$apGRexg5DM!d=lXo9tbh+JF$p0#UI9>d>92>m< z1)=Q$v&XzlHjsA|fns!s>*|jn?D XG*Th{uao+j5Dy#QRM}mT#+@hir#4oc?G%Hwr{{tI@BZq6`dQior zjLMz210{T*LV<<|pwF5kW^b5z{ vMS1R?*DDEvpmijv9vM?p}P z $ z&Vm_942LkLk}h*1_ij!Vklxzu_9;6@zT)7ocB6LP02P2qX{+OfSq9SQH*8UV{Mx5N zW%~2~M1cm1`-xXok?rxLB0eQ1L9{m@H9vSyUINO#dgLVwOyf~#gQJK~q6kg>YklPv z^1N2P%>CNrM^e)-7JR5@5jXjtd26;}Gi%?zeY 3}xuPMhxnX&{U+6{f K)FJ@A~F5E941U9<9Eml2yY-7*2t;f6d&56WHMxSvPEX)Kw#TYqWIZbZp*TOi{fV z+m~3wmps*3kA&>keofr(XPq~3sVV^piRKj0BXYDZrOpc!E g=np#0T>8u@eyr@GV$G9XLnfGjJ zJpwU9<2s9LD$v;ji~Rzh5NwPy%rYsXwZ=)-VkkNjRnH9leT5qo_5lrrky6$lExB5* zjHH438FpmAu;AYPG}WU-WYdKhAT`TvmE)0rf{vE?I(=Nfo$nnXps2GYMHvn1>uOL{ z$6UJ4(AP7zf#wWiL`NKe;6@WE&dIXbFM$@#Grx>CmvSCl`xqpet(V@cxU;~4xIzB} z`d=+nvl9DWM)ytFkOr;>EqBX;MhR0=!mKcSMm+Y?9?#SSFS?SdBW1bA1d$jC&?y y8z+YM0mYHMk=wYFZLh2M=HC|hio)!_mh8kE&KhS?9)lFZ6I zW_`$wM~Pbb4glW6>=Hq)D+Xl*eLihKCjoM<>D(-QP7%4a7R!T;uT%&4i2};TxZ=ob zuUVA@Id2I8fmAR5^lk!Vsfi@B3)QkI`T3~v!x zoM`KI&+5t2SHWAg^JEsCuVskC<3ITjGvaZFiL@IW-~TRw+i)9GIhk7V8E3{N@F4FK z(m?vw6Yn5thoey2)8?}e$t>{FMwX96pB0K>nOCAJG&9Gr0xu+Pmwz7!NsPJ3O>DHx zd6-AOsns41V&Z+y=VG9Q!NI2Vfq~X15JW>+xv`0JgIHG#w90K01Ox8bXbtH88EG#7 z&0cQlhjO_eSOf-*_a|Uo?R {1Ba66!VXwne=909a)8&~W}pnLeC*?9{QXG84! zi=To>h!Hir!hP5wYMG5L6IQ8hi1EPaShky{0{QIc>!yT9vSD|zKdBp?sVd_`ZN;eF z?bQqu(5=1cJossvuBl&OdAF#!vAH=sQ_U>qOukF?`aQhl_U_?9$!*I!&Kp3PDMfDk zbdwkZeveQzG;@#gR=)}Qyr^z1DJo`ZP!=ZT)AqA!b!Y%UtvnJ4NtGji9&$qf!e3Za z S`q%WAF99Uk2Jd&RvN#j68* zVAF9{tjF{UAalJKa~#l(%Za9S G@>nD%SjFS??aoH!bIQMu`e1PG<~dF^Z`}53(XbLYrA` z6l3R=u|{_0p)wx-+x}tAzrW`wy5MMpbb$w{ E)6_T@J$)%dHCgEEp0q)b4twpF`r{4Fyk}JS`n$TitnWaF z D(u3DKeFyz~8 zYHtV4P83Mm ?LB zPUbec+5?;&-wyvNsW`g$p0#KcBt}rw7=6-B0gPM(5Kk#}8HJs-vGlDp(w1uuLAS9t z8w4C^rdI$}V*Rq#P66nyf?xt{z7&`v;LY+YKdo5Vd4A*oNl-)xC#_bgEr{vv%^^-l zoFtz #Q&c3-zoTS6#RE1{1*%U3y1$d&IngjL9z_R?0sE} z0-w9!Q-O(zd`4!Ixgmvv$=~-D%8zjdy(I0e@@9?>)2;kMv2L|XMq|4CQcP#@N6}c2 zh5hSp^Gq;O@G-O@PxUTgRY%Jlzc6mFTKR>J8^5qV48F~;3q%+yuCBadVdWSY@EQOf z5MfMZ7qD4!D6D;jav$=!3X=2)k9Ym(ZWX;3Ib0B+@{T{0p1d2qt#G;F=GJ6OY}HsN zn@Npk9T3zzV#TdM;RC#9n=R%$5`fHwFcZGOQmbr!E )61x18arZO+ugxV<}_ivvSMJ974E_(MU3>d6uAolV9k}NG_X|E*$opQEbqr zNxLn*JEt)tVg^jJUjPeb$Vhzh8$cf=fN6a`42(~9KsjbOQ8z$&`Pc1k^B9Y0gPA5P zLt#JROU*(+rn#A&45@_gks(QeLsgBW;X*sg_z^yjh)C4aMn)uD);Um8hy-7zB~ zuX>)*H4R5)+@-q!Mh9fgsgHMq1O<5g1i)Afc#JQ5O^hy-F&w%rMNTgdo_ND0(vNke zfEqJ?2lT< 3%qG&k4p5wgx; z{ZFCW-`xCPoVyt&pwf|Y>bLgUKZrZ?Yrc8WlDMCEn~9;__=?qUQl4T1;Z?9zv6gCi z!t$(qSC{t&Er!Bx3_>T|1+~q$@HK2^LO`iuw3`oD5iODC5n*8kUJI6pmLhMPlsO*K zkEZKB2E{<~%Y#-K<1e(f#E4<0yJiQgR|Fa3{W3PcQ-<=H8r~e(wZeA=S1(tf2%k5( zy>D$jZtLJ3-dboPAo_LIE$UYZ^tWq-(wVC28QE7a!5c7qKsvm@i59DzuK}u=g$vd4 zLS#zAF~)+n-}Af+46&hVy7xglwOd43xP>68307H<-IJ$!n~?Mlv%5BFQZbiU>Du(2 zGyJ7SX~LJ)UbC()4|+SUUG|GlLzV$!XKbg<{cO6Y9BmR`F+2Ym=s-ZzQRCWK#-JGe zwd>~mQw4Pp_BPMw#$dwnc_tLcPG*`2&+j|ENvz<=JWQOQKU2| dpTiN2&3-puNsu&Kp4o&29j`lA)<(oPD>Xv%Hm>vK@n=9=T6V$MJ?08((Fmve{V zD?W|4W1m6k05lQ_-h1zO;*yf`apcGVXP{s74nHf~dxwYl7U{rA^vBT;<7VHHD9Yk+ zS)yz1_!=)&z_ZwAl~SPqxtP8@V 6F>+{ALTC_i?FI0J;ut%to@I=;k@(Wa| zp;UI(nI7`8J!$_9WYy_1==SoNWxCArd`*PPyTnRtL&Hk6D8rzhF#HFv%mb7W@Qxnu z{-k!R-{h56$b~_O=8<3Xh=EjAbAk-+643mjw8e!^wyzqn9sp`1($L3(@h|p0wu9 zLb*+?HFZnLmwz4VvKa)px0QsTVS#o?g+!Y61m6?gRq2QG{RWiyRr5lr>7S+B4b=my zi=GJ7(3IHbPK1aJMr;${2(L79s)yR$w)t|$ib@9-^feJ^rPtd0X!vW;NCsj{hV%8C zE<=tC5aa?N|Lk2QX?_{yf}F>8xa@WdNy)EtqFPdM(P#M0<{hZjBYh2uQ}h|S6vd^W z><@LmJ)pD$LSh|oBzHo&_x*E?YuL#G0MP!fcYY@iR#>cq!agR!W1A9*FTy22-S%1p z+|OSZ_Q{ZiDU^TjJLL*)CMIuVe@HRE05Eh-Gm5;w4_`2OObQG-#}CHs9|EequPPvQ z{fEPwiRu45&9#AH4(pDCJOByMYByMUv-2pE|JDm|9sl3Q|8c$Wns2p6`i4B^sEs5p zePr{QyWh_Xgu@8R%j<=Og{7bt2fZ|%#e=7U-l~4L;W(ixSa&YmUl1*OpMERs*J-8y z^1tEY!iuf0N}N9xBmdxR*b%fUv)NY1%QGw11Kj;-7p%gLHhq~%+3=-bb>CWpAp!)2 ze%)!`F*ew1!4JvlGa8QJ-2sK@sYO5FPw~LZ-Tvo}Ot4s1aJ^^X=HRQpo}S)J1}KvS zA*3T}{t(Sub?4|O!Pm3eu-Ldb0&rghZKoA_%o-Opd*{WhNZ2&X0e@`9g^QLa(PwTt zegUsGyx|sO*m{7KxiwK1Pdm!`_a*pXjC=%)Q;d8hgagKa#*f#hN+yhPz#0M?HCpjE zXIrDikL5rHE4_<&Qqq=z_*_(2$R!3H1R70+KF<+!qZN3z=U#Odnc9i_IrwHAPhEea z3*KXJH#48+zWPYF+>N@vNRh3mSI;GV26mI$SBHyCwr@K1_he|D@wTsA>e2wMjw+X7 z1)^vwVtW~n!A`udd3gxDiqNcI=&T6b<=_PnkfE%5#=Zx42LmrV5=@z}LH2{(M+Lw} z#3v000*GPF9iH>wj)HYqc&+UHvA^6sr|2%%h5c@3`O9 =Mg5 zFgDFT#4%d9qctxT!F&P#0%B=$;fFE@)#H>I05YI^TqN8Co(C{%lOBP+#l_d-SiMbY zK$mrnWS!i9_%y=_!OFoGIq(v6E#nd=)nk7z(pf&37cjo@%X+jD&KJOb`HfAd(?tU( zXO5T@yiu0cb~wAo#>NJ81hnxsZpXaJkw8TT0yuTUz|hPl={S&7?SUoYJ|0-f%z&tB zCd3}rzzDI0*R{ZHD!0}>8hKeCtZFsLXm-gb-x%-!E3j#ay#e4^K_`TxNkX1X Bxr!o2U`q=$phIWR?x(7Gi@`V72Iv^jQJ_o=k$4ddON%{h(eN+A8T zI~RZzpZQrZ#&Enm#9cASt^`sd10Md;61Duj>n%WHA?h%kHN&M&@x7l+W!VBDcm5P$ z58F#RiasOdeD(gvIzawf#WZYSpbz1bhwW>*yH(gC8V_bPPQ*&^A?%CyqOJF*sIg(7 zh(CN7?TBpP^_(>5QUEg5SVKt5m=)k+O5aDpi!|L`U1>#&6}5~u6wt|mfn3pNX%hI& zO^TmbIGwmiI>Po2a_87<6`nVspQs8qCF6l#3WM9}&xJ#$(+7ZkZ~<5o Npen>^~{$W_$|(zl=X0IgtY!6zJY$1BrpRI8Qv5^>Am37 zpeF(%uT)Ol#N_2a!# rwzB+xe*Mt0Bp=ShfQ*=*{{|VLy$V5YeW|(~ z9`OrAm;+ycp~aSyLXpwUR(AnT^il>umIrV705|50s+zF`*Qv0#Eqqr(!CNMR&jZj! zg3`2AMS+Rn^Z($Gk&zMDWl?rn{{!Ecm>6*UpOD<~*PCmX)+_&d?=|K3M^E+kE{jxC XpT^8#BWduqFO%9`?K}BOkN^CCY??o# literal 15382 zcmeHuhg(zI_ATo1U frsDV)5iszjB?tSm~{(^V?z8Fn*cJ^Lt&N;>$V r1|QE{xx^*qlM7acHC~&UMBTppHKfN zQ7Ir*$(o{6I%BQm|B13yLh)%J2Lw>013p%};H&y-)|rmIzMXY{#hJE(m6&+e54o9j z|MD449*HJC`s gO;4O(ULlvWHz1$T6nMphc2$nU6;*J^)JJ708WK?f zIx@W1`7u6;2}8W1v6T(B<7@KL<-0jo5*#Gf$ug3w-_LT_+PypPy;)!1SdTosZ2E9K z<~A)S!q4kXt`^1qZf`>#X+dXLad6eOQC)9s!7kC+t~>m|#LtAIJ$nL#3{Hh1pR=#y z?6IMC#X5V0#|2}oktQXi!AF{zcZ;9(IXW1gbQ&J^F)aeO+~%6suX9xLN 63_)rFMPWE($b=)7jtmJxkQqG*goG){Ji1 z61tAoLJpv6x)NkZ_obcxj6{*DrW@6D+;B76f`?}Xiv-bo8KO~vq)eoYbKeIVp C>VVibRtCz@lDX`!mrqnx2Vgxg~dBc;1gk z&ryS;M0dKkWu+VNCa>jk@9$kE_$%iKh2}?`lOjoYvE1-nxZ-8J4)aRAhh%d>?VzgN znswIS4S3q 2!a`Uvw3xz66OFjCqeYD7a=XN4y6NZaxF4$5LT4!J+ zZL3~MI3j^lxonOnHCP-i_6&&rQ> sbf7uDk(c| z(JhR#s7LRr<{tONY8*h39!XNxcEcpzNHclujzaYAnG4pUQllCh4rL(xLtd`!W}VS| zg{WD$k N(gNE@SnNP3>S8BJr*Bn(o=ymwKM_TOSNAUT`AJ z&ro{kYs&V-`@?>)(7^cBuebCsKQMYD>troi8cetgL+2EiXsNMve9Y?De0cuYr3A^R z!}E+h!KkO@-up(#2irKMi%j;NrtYRWXifK>)ySGS{#}-q5ra~{-MhuL-mquAB0Xz{ zI0_R1WlZE2yzaEo9&^oqHA|pHYdd_xqR8vv3ksinl$av)+^-8)5 QEsC7yV!}dVCpJeH~Yvi1) z9Kq`@L$^xH_R4yTQ}?j?#_Kq^mXK~{&+s-PPd~RYm&0Q^aE Wng|Ge_=%5#&puF_52Mtiz;9DmvV{oP*`$LjrvA+qapelgJr@2Cb! zORi*vG5JZb`m egs$2&dVj7|6*xBu6U-M%-%I6svZe9&lWnj1+(a0OTndM4sc=XZW%Hz*ZQY3AM zt6XXwy7+73Rb?iG|2g<}c;k;qPL<7HFD+ra>(37{%0NaoOJTc3-V4LtLU89FkCIaD zhYm1=JbA?i2PNTXHeoI?BV!Z|+b^@lCW#tLUb&(nt7)c_exvek 6~<4RC=`}OtJt5;&CRnDVzG%DV8BT~t!%V5O&2v{Mr zi6s%no&d^dz2xD#y)E+bGnemwv1%5|4D8ls6EP}r7KFWNO3ckE(R$Iw^ (=p38YLdE`u90!F^Z=c7({P~9&FCiIAaNC=C!Ez_d zF}-+UgF?)#K{!Qdi2snE#3J;N3O@X2iG$^A tsr4YXjuB#e4 zA!gD2ZLQQ4x%mFp%kJ*(Nw}u{zkPwYMcrme?ILQWO~e*un05QLI**^tK#943UzRF7 z!*3b{R$GGbqq#&Qi>^or)OT}f?2C1aVUc0+lW#97pY^>{3aEmpxc$1hu}GMhm=H1i zbXnoruI;FCfi5@6tlodU@L|E$k1RaX0fr;L6emtGggm|t!usalk6(P?jipm?1_s|y zRm;pnH|?lS{6;o5Ho5uvfdYAGW_z% zw0ti&(hw8?76ub9v~Z~HtusF-WaIxjT7c<*ocRa2gc@uZtI&MT(J1HS`NR&T!Ip$W zWw4kb;!9p31Igg@Un2kT%R-SJbj$WO2dP#Y?9DdRL z4aaftH32xV5^l$0jy< <1dV^ylGzc0Oeh4ut-zX)thW?AtU&aCbGABq zb?NHcw5M;_;85lEkI7?f@RhM_OLXwE#i2h>99c3xbW=3e=f&>$Y8jDR`2~qJWL?~U z>@|CTXBlxF$M2LEP0F;@-Go?OlaEbH8>w*YQ3=qn?fdvBCHlH4zkM=aOIBfvFj6iJ zY >?AYUidZiC@ecsse9bzI?UF0SE|PuJ2|KnZwlMm z-x5iWkBbvF{PeiOp$o*_ lCNfIr%1A!d&ahG!!C14+T5e9OogAh(hU-& zqT)KiWp9Tj?bP49@6<@@DPVNCuI}~g>vNEkiK50UZ$(OC-ip3sdRU-a{2=Fz^J5T* zGnUNTQLSQ3t~FChuaC3Op$z8wiV%KEA%kZwFm+h@`=jatu}NYg6kb1yv6C%xo)&^f z$}Xa1>eBSQ^J5CX)OzgOa)L{ulu#gz(3H(M3Q_>n(i_aqUqcRnirDRi+z9MF*~6M7 zr|3BwW79}3MT>jU3VQRJCl=CTVxH_pQ>R~P27s&qmF6S+l&X5;o0AXq^}%|G6;%6B zMp5+!h5D}b>-~GX+vuGYY%Qfh;bxA!a1HkqqYD+aOT|yUOJVXE^_v0Z{E`#n$@2zC zh&f?#q~A@U{PnT3ae;ka@(f2Q?c@VZ`A^2LUc2@scz26DP>P_%XD}yMQtDQtv F^`O`Ukj8!z%GtEUOx_&PX>8pRMW}F$Cz1Lcm{gUsk~1$q zyg8$HeV_0goCd?lX1;-4XrIO+D7}RbQR0a^v{icx<_j{1S>cHyM$}zIzwLsSME%9p z?_lNM-9<}WL$}vD>})RQs7FJ(bHjqm0q4kDUL*COUJ?-v#IzWlvXRs*Msdmw4e6QZ z7}K@!aEboEH^GAk>M+Na@)k23E1>?4&G@>|1L4ER&RkuuK&fYc-^G=;n^9! `J5GhOw-t!;G5Q?AJ+utdepO -P+ICAgJ5$TAOw_E^V|BR z@5iSlv~0U3U*H3j=iZRyqva~@S J0b2H(XvBgl!px&M zII+Uw;VlzLRLk@up@_1z4}YcIzct@+Vkcap!J#`BlV`ya3~0x7MiNM~AM^^&x6JKr zuv9O@up(*CHJ$&J!jE?KE)ezL$3FUYLUEfyYvmAxIvfvlVVIQumU~;bb-K1yR_ONG z_OIpp8J&B#>|o4W_d-=oWjaMe&ar9>Prh<)2>+hUdcbC1DRcK_`yS6)?Bo-!yM@jf z#thd4lMPyr4(+3d!N;iArb6a 0qm$CcqrYJu^_X~9Bmjr)>`WZI? zTI0tEJ;D%zmaEvWJ_gvpj0)4)gstTk-UY!6LQt$@8sns7%~WXPO<%!SZ1vQ2+#z=k zX!79 (&e-(k$lFFYB@>ocE1iJP0`~gTf*qx}qWy+E zDX^VAn}qGc3% =H4Bl8SZ!HMILQK!dwuli(cSG0L>sfp&XbKJY7t8D zRKJZfh1aIyJE4zRHj~OFEFO0j<45OtNqP8%k}=jz#XAB)qvEL9Y*1ZyrzBvp?7g7c zFX+}>R+q56j$(j1O#W<+kyG5Pz49>sA|O9Xw@(Lj<|e5M5lcsQjIUi-5<}z&Nnz7X zxy$A=xu18cUY9H&x5f&P`-=lsFhZquD{bitK6Y~n;qGfIYB@*Vs+M0))QA@}2b}xx zake_@uoFVUa&OqF^bx4wH=iE_MVwIQSH9>?ogbRX2q*#Rw!FMdKzYS0Xd|___O^YZ zm8gTErv-9X2tGtoW!BqzdwpzH{60n~v-jg8t*ZL1xgvX9TA`1_tg dIC- z&MrPuVqSZQk!hBC0aoFeDhIWiYD)}1D?;6zh|W~>(?_;Md_|^tjWq$1`IcQGBO_zc zxn}&WNN)3jBt~~MaJ9p!q-MHXOCr%DH7+ijvsh#bYcl1df(im;81yBHDKX`+dj~V& z4wC^^tgNi-2nFE;d(AZEAd@OF%Z9*0{k%!&&&Ym9Sru^{QDCBa80OiN$SR}@AVi&N zR%WA7*qTlHwc*|paU`-46o (^3bP1r*Jn;U~{u|4E38i#sn15tSE; zt{>3{Xx6u&uZMH~5y1h_I?00b<6wYbCD#oa0IuK-f=@KBia4pS#>2i`&%6s#J@)33 zRuUID8DLHI0UL{5IdBqNdvp>+=iPPMH!a~tIJ0a()XC?r@?@K5(aKx1vt2nI )TXc$c%H16?_hyMVvhfd&VM%Q_awlO0PT5h|S! zxltvx=A!7%-qlVO;^(KTevgB AO z&Cn2o>ju}jPyLAIMF(s^>OasL4o5^poS &d4#lfqZ| zB3H5#!d|qWOYj-J5B7+H-nXc+%$M8nC=!8uVmd(0VZNa7*RJ~O0bYf GSYle6d>1IVcn0iaiS~a95fX z=v jsf6NeR*2!gjw&DgK4JrwGzma#cG9V z3O=}+ImJJenY&sy*4pOp3&K!~-d}c ;i*_QF(H`y{KhVMp3&e^c@8tpbv K zrCTF16>J11$&2*Dv7#gv78V8qHj^GP9sc N z0@wSDQm;GmBaHJG09|k-pei8 u|dcC3)5 zpJzwQD9a_y f^1F)(#(2yLE_gk?cocr}V zZm=%GEcJSd%Xc&+3X?6 `pGt?!S6b~Vl4G~&AS&72v{T+8RsiMsercLF#f^`t{Hs)$*$?_F zG1$;X(ItSGe?#*4)#Hlst+pfX=VuPoM98%t?7ZmCflT}1cT@cM8Mq1%s_d(OJO$o? z-=6^98UB9UJO({72?c+=AC9u}3Gv-#66oXygi0*Q;v2*o>Lgn>AEUrOd3EK}sc|>Z z5-}^S-RCO>IuACRAmAEfS_KvtH>OqZKGil M90gFi?-~M)NETwlX7ktIZXVd(afp-Eh3}%OS&f{3p<2;gt7xDKO6K4sJCF z1Dmd{U0J)%=C@~^Kvb_LHwK&Y!#OJJ`5o+-by0-H^Mx-|dVd8x%c6GjW%1nouzULP zk>9MgsI*}VW}(la99aGPGM50PreA&f!jVH3l2&0e(_gpzgA>qGiGrY^GF$E&p4Fg% z^Qzsme4=%%#H_|0U?fnk+UwFgTvQ+fxMJ8 1VJ%*lu&R z)jF HdThP5?&7zQ4BZ@2iGddr0QJw7H9k zyCi{{ wuHx|XeLMN()QH~I0o;&Ea!b2Q=7B1&l6NYwhISv&rE1w* zKR ZIBOykm>hSQN` z&=deH44O(bViPv_06ZRWw_SvhMQYlUD8;YW$guPMZy@SUH{^E7miuv?ez2{cOktxE zMZ5NMgjs6Np?SZhU*G5~fV0wL1_z2 68i*YGjwL=Y+9bn!HAjmG%_Ljgw6^nNLd6%h6oV0t4$5 b$ WT>f%@cCizj5Tw#PEc*%g6`$6ZWh73m}Z z;v$+pm6N)PI28=@3{e^=Hr;qAQK(2Av_>iAren)5;Dg7#dURwlQdV2!W8e8-$+aUS zhy#|eVzPd0b31wUSkQ4MP8n?rHvK}Q(u`i6TD6Rmw7_C=_btd;JK2RUA*^Le*+*F< z#EX_@*jfNIbm+;~nO-aj++f`tHr|z{ow^Y~b@mga)KZk~YuS7=pCbW^ezwmZE1o zGGzU1TXzI-TEf99H ~Q-IOS<3D(@_* znj LcrbY%l>E!h$? rN7 zA6e}Gw%|~__b%{mLXAl*zZ%GcCk^a!(Ms4e_XqN k3lqW#Do0jq3?m9qKbRAVRM{s1M3_9Lg9C7hJ zw^@4X)W=hF`KgOy$ucbs(Tj3Kqy~&sLnWnY6xco<(AFnBH9$J3@+5Unf4R1|w}Hm~ z(YjMs0m^_~BC)<^p=DvXW?k>zy%@b(r-72hN0Msf?!53mzUClMebxioad*Q8K^Dyo zjWsf>%*$HsE?&H}Ms+xo^rgDqqgbt|9=5;dE vHm0Bq662^*=qt6 y*Xg%GxIdo-n<-rz^qLOksm;-g@KhOf;Pl>*wi zs|VYP4;)Ta4nqBP=fJ!91+?EY;%3!AJKbX6yu!C=#=}p0Y}~FTJe^YZCd~O5AJb`h z2#O@QdgMUpW1B~3fO>zS;1vA9E4e>H(r$CzJv|PL67UFNfPP?LXqBRSRsTHB{6hs; zZ#T(DoNVHYDOeWnRK`7ZJ|f065o}-Ds%5h|VCK V%;i^-$FlDO?`uytb_ofF(7?DJRXCkT9a3Es*L{VnE$4M z#G4EB0aaj1w+Nsppnjkm3IK$;^zoP5rKrz2)t^ 3bp84$zW0ydrD#7*wdnA1p z%^Wc}$3kFh)%mSn gCkw=-XKwd9?<3iUG0!1KVtVj!%i&_Fbi#r!7LH| zX42{XWD?4Yj7yPfNn^u&2A9eXYP#Fl_l4og_|MlkHSJmphQq~Ct Auj1fGOnYds{}yg6*wj8*YxF2n0L0e z#;Ab;PSw-W0@W({AJ7};gwyv?`_q&&d7oJ3=rdXZq`08Pni*gToa5ysg7pIQeij1p z3DAwv$kINwU`WY2k5XGH10)I5eqrOXTtMR1(*DvHrmFmQmRs~P6gjF1F1_8il-n^M zrg$>0JL*3!IW8_Ye$_@Y(C^+y!(x>Cl&0~{e0gWCxY%^Xh+E@Htt3%-@A+g!)Zb0| z&}W@g8NgAbYgfoGnU!NSQcJLnivDY}z+1z<^x5Ctz@}aCAl^*dj!l!I6apcL{fho% zbTHb3ickd{q`Ho`6qstzi680m9)cc3%bEqLuT?mO0>E0N>~R(6fi}Acwy(`EK9-3? z@(x>vaO04rVC71=cEB$(%b~vzaj?q8G{qTH8{8Cn`ZpnvzH+4yoD=X}bI|(%mu{Q7 zP=qG{;`9?(svxpUKyU^Ff%v?Em6a9f3#h)A0FlwLMQLm^|K_W{zr2wTm;uUf3~V!F zy}lh*=X#^fyq~$hV-HlM#0_>iko(dLY$@ZdGfw6RCL(M 6`0f6 zsSuO~taC=ubtSkEq;wj>U? aOjAk_C=>p;Rm3820g}^wx1ewsOhp(L9M9$@H+0 ze%3o!W*ZKiS+kNTK%OYnv-_pxOvAa{3E zxQ ^+K*7h^dTd0zT$boBmu9uK>{ z`|UZL%G%N;*7JU;qJX7Ww4Trwv_LJU9X&klqY`ZI{>~Lv0H`J{UFXlyg6XOSKyT22 zG r!U|>?xO8-h z9heK 7ST9;CH<(xwn( z)Ot{0$j}&Y20hJh*bMPJ>CH#~;Q@&{FAtYHk%C{+sr}>YNR5Z>voc@_fr3(7Z _0MNKKVOs8NUF2UHK?0^|!~kU;?72GK zvF`H<82=u@1+z#{K!84ykM@+KoaW00vk}mgp`z74kzlv$Vm2gN-!>6VFT2gIap>XU?-6MsP`ll zkVt?Vw@zv+AI7FO?^v{A=uJc9s?VQkk$5o6F%hM3EC7=#E-l^n8}zepyGDzri0f1D z+PExMIaFpU1sFo6&r3gRO)NIAr2@3^1dTz7B;{K%PaLq-02TZKY*b) C#yb)*pWRFAS>aTpqO6pU`saE+FbWeb>6w^UAfsosIcL zA31%gLhjD1d-v`=26_nL*o6zo5qBr`3}9ckii}uw7IXW7KL0|gTeuR^d-4ih Pv--G3owMx&4K&$1OI!_f0y9DQSjeH_-`%vZ#n$`7!eFY z09anoTV{pt1J|4ao_Fazv?rAotxBH{4cXg@0Y^+aKHz{;jWQWRj<*HtsAoAuss@dK z|3t~vJptGn%GES>7)*}doOBCs^s@|9aqIsf&f4$o>($|Eo3`WO0yi?;NUK_GmQQ8Q z 0Ujk#ibh~}(1b}~j zxG9%64|3RJzV{ QC#R0jK;^q*Y~lLJbI z# Kp zr}V|4r;D|qvt)a@eXhPB!?HSH(3Ci6x)qa_D({hx`_8BOrgeS^y%z{t25Qo+9_ynr zOcRo;=1z~?j{tTO+bG6_zjSGTb2Y4VBaJo<@3Gu>yXy|^D0wL2owj2`{*t~O#kIz@ zKeb5;QsJ$)-gFPp;w6ztc%*CQHS4?j`rTlLD*y=9s%Y>M5oq3ELQ-Ts6Sg-rD(T=a z%GZ?DH+~1~6VD_Y+$FAGQQnn!h*IFPv)tZzoSFI#=GN>Z6pvQ9#M;!H8YMosiG_1Y zGV#hoXFf>Gi<#f!4+djMaj<-~6$yEH8t6eRQJ#f1xjC+_Ymdv$!VQn@UGv+i-v!3A zB$9541CbHsz3?t6$!_c42JAeTjR6h2;Jz8z$V;`aNM11~zGg&qXyVs;hTt>2^T^F7 zojw~9cyISUoF(gWk4p=it2}iz_MuNhjI-;!KwmRx1>P`m4}*bWpv;t-*Y>9*G^8z0 za?1LPjzy^2tAV{U_}8!-0d$F@^#Q36ppg!vyoyL9pZ$j4LE8Y`+(R((1j6=vj#GVL z3NBOBho;@Q;t?ncpBB{J?PWOpN=>D7Yp-{O8yy_&JKI`lm{S(jIAeB#S9xS7NXN=* z984_Fi%EH4Z+H4NWyL#s7C8W40*oc3muMv;M2vI?^6<2v1r2&`EJZdlPVZb_f-zEP z8>44U6;r!$F1iXrtWC@VyyO*XbSIAJ)49eQ2R6W?Ny0m0m?H1hvTEEicAXt~+EUQX zHj&am&=FCSls~ e+M?#6S<|j#Y@!Y<24D=h(-Yg;>P9`m@;jPjnPvjNlwwm= zl&QMIK|NGaqPu>X8;u&jQQu%>azhut8vU9?_Rch}C9lny7bvyp$#OTe4UY^!*cE&h z({6Zuv>8(Y;tOym>}SZTN=a8+vS{M>fi^V li28N^f}Vjev~G zPXB1Yj|I)4fGYA@eKc;5^c)jl=&V5NTa>a~R4(7z?!naR@~U{rJiXr!{8Z3OW=&xc zuEP}?62Lt6B6a7L`3A3ws77CEhdu^<4H& 1SG=T|9 z>h2;CaRY%9WTRjGwDC^@f6pEB*xU-_iFZ6&$rEn9A0B{tO_^{oa?PXQ?F{CP!SX%^ zW0atiGu=H&WJCWB9td>Mp>;(G9;HH~4GkbaaHRHC=sat)lXi@MSFN!-VJiJJ$KMx2 z5@^vQj%+^!iOLJxSyo4LUbA|&SLO!V() 7^29pW0$4Wb<^ZMsLn+PjQ7ht zLEvKRlf+Dc#D7nt3H(74gH2`6gTAnwzBb1}Ov&j%pqtbCRbDWt*UJLsTGFy!ut@Z- z$(&-<=0La8a Nte$gf^WL%OYh)p&cgkr0SrCgm#YXkz;LQ(rzuM*5+GJT{ayM9 z_upgO3=E2NCdWhX_?W@RL*M|lzS4ip@E_CO|MOuJoZv*0I )6wn>iH~xMGg**!oorf29w!xj3Yea z-Va;mv!NH8__9MT!d <^r<2;ZKPZDb1Lue zc+@&+$AH8CdiN&R4 _xd}I*SO5>)2$;!0dsqQ#E^xrVF2)`T zRc$&qe(J#mp>a2$4_?wtxTmSPg&|KNER)n6d!0IJgF$owT^)qmG_UC!jp}hX5LfQ6 z;`+f%L|bd?<0@xkaP9?N`H+9EhMx(jSfgN=M&6WO>?@s&%m*l2&Ef0_;YZKF1>hiK zEG2UD-Fa9rdE|=pcL5$L81;~J0+xjvI4cvE+ >wWKrO7>z1 u<> zZkXCUu(}Cs>iy9GipJGXrbyh7P{jAMaLE{M1@FOP3t1rS#$D0^s;D>k(E>o7joB_$ zBzBhmI~kyBVX96G-0dwg(nD%}0t xs=J^S(|8M_XUh5HDH9F7Ru7cAPVrs={8|N zr4gV<4?hzCjrncD7}&KU@|L*@FzPUI%*vZzhs4alRhpwbIi#N@5au2MDUfbH051W| z@oDJk$sU+ygM-1S^>l7v5(R-VTf>}{A&n%k^m2a(6}T|4^DqE@9hOOL<6tM(O;$?~ z2)glZ*PguGlJpb=!$_4&s`j&~&5ik?_}Ey!R3fy6+dr|Q6DVO#VXTrt&S2st2`UFH z%r_@FHSK47;6P-41~h9KmtoJf*)A~PUjZQu4-4OG;^XJ~Yk@BJms(0-mhDTYJ5pi5 z)UZojHJ2cifVoF@%fK#>71iVS?->G^D!CWSkFX4;xgZHuZezCz4NtxuOu?JGV=jS6 z<5dX`OoIB(^%mZG`DY@as=$hj7t-sG1nxtki&O6hC_O$)%uVgu`wjmUcU*59kBdY= z&z=2yCBQ7`fxoZpKXTfd;qPo#lyl0w*fJedFmL4C8~3F()W(c@Unyg9auUo!4RYR* z=ZKBK@(7>Ophq Qas?p&5QKH(9LXr z&t36Xy1 FH!C@KOnDoD4CqS929UaT-PD5ywp0)|i$s)QPFEPx`Qqksqj73n>J zfK(+EB~&9J1;_vb0tu0lL=pn;j%CU@&ok$F=KFr<_r2da_aAx#_s*TY_F8LS>so8= zTj$J8#Wu=r6cQ2=`|*cU7leeq9R>HS_3OaDd|7*hkdXV0A5R^>80t(%iPXqjEyeDC zx^-$t!}YCRyLN2c_eSzt$ ?6BKh@lxE#B~w u>cClx`#tJf8yQ(dCM^7uU3sxw)owB{+nIcz*R zxna7nNRRRA-foo$R=-UM#i#UnE_%Q@0-mbl{WibDIbgU=z zhOZFLqS#8SA59^CjY|{eyq@S447z$S&@8WkMRZV!pu($zDSg?dHf)zwu1#tvk5O$i zpQu0k{yS# xoOTDjn}Yd=cGDMS86mZ2SQwc z^vrqFMP=3qN~ITyT{YlbM4u^Z8s5`5*CywC^w;VA-?gy5IeO9T{7J1O*}|Oso$01x z{TJTe?aGev-tYZt;{3^~tw%2} x(Kjmo*O!2RV30GY_>KR|~ zcZPwtJ+Z(2uf1)ZcY$}Wl%?Qp|L!mn@r+na?FFi0AG-9H9i`I$%1hr~=>JcW`K#}m zpHq0;;f&bd81~QB;UB*3oSD^i>_b`bwtsgR4(s9l0uo)c^JL78>_oi-RA@i<0EHcw z5}e$(WA?V0`lf1U#DyBpw2>>Fg=|I+$Sas6>8LtJyqp_(Gcq}lkThiR5B25G-}7-w z;eVkoU)1c&uw!agViY&;zccJ#(#}69b!F>Y!Q1}bVc4UX?Y?9LYfq} 5Z`@V XHlqQlt zbXsP?4-N+WShG?l5+fPSw;bY?M-=>7c>mDY{z9b^5Bvig{l7sIrEn$3udjoQP)A~w zoP%CmT{H6RzPWckL;H&?Jt%=Stx^t`d9n6T`{(`CQ2SKs!MHl#78%!3B=n}2hQ)?0 zxo1REX@_+)mN)^fy*WuYo(x!Qk}y~~@z7wT)PLwXFV^+CMWJYPefJT R)ta z|E_cSn?C7xfBUbgz+<&sfd~9|hn1J4wff`PYhomnoJB{ti&OU*`qM^E=Vu5 RX;{a*0d0Vx)_?Emz zN$0S!k`ME8ez6-Ynj~X}j~sH;<-R@`MQ6E&u4dhckIl4^7D{8Uku4Z=^Z6IHk>95k zyodgE6Z$Km{NJJAYP>=0tikH?0&ag^#xLU=xN?;FOUUXfVL5D1$xgdcc#>zD-6ra7 zSI6x6!)DiOW@6Y97Izwq4A^grM&3jl$=vbE;_pZ@=0%1wF#{70SuqDcQf`eT#^&CL z-o%6JtiHodf3qX`1DyLmcsrwT30MZh&qcMm$CDZ;?dX3TJLKC`)>xen#A{a? a#O}RPSlD3{1x#$%-g9>SunUvU|5yi&UFRk!KnA*eNt*}1M zj95q;Z&hrBp3*6!Wd^UgYcJKZ4NqqQWVulO_JY|>!Hw(dJ*k`cT$ZSay#Fy %T^EB9hxIaIHipmjXc+f>=osa z>zkYJwX;NVrQ2o1jj=8@Q>BMSMh1*GHR-ya2@Rir=pRK>8d!U*P&v*4iV8-j)9oH& zL8x& 6coijrO>$2v=S~;JHZ6~9koU*Q`PB!IeCrs~6NL2s{~GuH z?y&wvBno!$zv;gJ8iG!nU;ZO_1{i7`{LoPV5&sK>|7YhafL`z})fk7SRX#J_`?<~B zad9M%-GyyE`gx16WSpsS+YH6&nWyC(A7o9+Z*~szy6|56GAxu^B7uu%MtzstxZ
ydDg{!b&K=Lz8gH}rGxU3(@nSsKW5{?Oqq_-LIs&Sd_Vm9vdUF-xR?%tn%Q$6 zQ2aQ~Ots?F$Azp_M>(U(j36{-?8^UW`#?p^K0j;7s3hop8<4V_j7^NW{2Xie6mlBA zT5i@8$KyTbk`3jlweVz@y@=+uo5^LabZ@DAS4HeTxNac*J TZn_Cyu+O@mO4b{~l1o(CQ6LsSY 6xpS5Rs*k5a2=&Bl(Dgcxm-K(cFBTa#|SOr ^eS)@f8Z>sw>qgLpL zgqz+KTr|3YO;IVXXp~14)CQ{Y~ zOx%R7>|AEFDBUz=8fgxO_aI$g+^xg8UZ366WXMYNAJh%GA;+vS99->bbp0^maI-0s zQl`GVUsI~EbsFD6FDQs$HiRI`YBVB8L|vE0Qzc>*!##&0!ZCkEIDJ`>zZ0AP&5-`T z$;Mzl9)||e{M^wfkESTTH|M%O;2WpeC(q7Af7x0!;kuP~&z`Jt{1kW11j>OmxZ9Mm zx!Z6$*7coT-A$1dva!L^`|=O(YDAkGv=tT9^}}NQ+n9(=(foygU?y%h=f76(;64BU zBxO(mZ89Sk(Ok<3K>oH1c$v4?bVSGJYoB6ikH7eF8}gAj%~%Ot HW=-r3*OM!pBbM~W|G66=9?P0%I8xhYX-&~mZ7c>=dzs-ET||}Ag7d%bgf=o_ zqvexD|Ily#nN9u+rs^M}Y7ofFN&P**`rqb73)a}2rz*cE i=C0$88t4AJW&blp|99#7zsd6dyOH1DMKSQMw^xhuY(+;m zf6eh#RCPZy?f?3dWW`L^H65Y%ai~BsA%!PSLY0aXnPW3gP-{Nj7!uXjj|#~iYhQCt zg7M8`R&TeEy#!;^r<>`Wal)S8D2N<0tQ5UUJgG12X;`WG=_WsRhxp?SSLB6Qz3RU| z|7oD&HJz(_djo{to~#r4?fFkP;owD&{@IJJE51&HX39j(-_N?-2y%t?Ly+Ep#Z4ZM zy!NU+QooYg)939eEmRVVAYuyMC|#71u9f;SjkhN^Yb&_oR1`#iy)%}muU ly5 zEErvD<}j_EmEHgOOBO>dbByy3t6x#8#K=fftT;a-h=x-zxhXm?LjT(UwHB#vlM|EW zJGxKQFe4}RM?T;rdYp|HIyLp|^QO)!#HwdA9If3K`g)#aX|PuieNINEMN?-PPxH>h z JASIKH>{ggV9hW>@);eFq|Uh*8L^l(tUI9{C9%oN_kB2g)ng zXjuFuk)rk_A=vQS0Qj|=)jkzFx)JI@A`*)7vyD7og%ZMIZlZg*dbZVsla{X?dGT>w zWTG`8opRbDyJi#R X>AZA61+H)YFILvdBGyQ9Oq0 ztGhd0bCRt$VtR}sNSCgQp*3X!jh5cs{{RxxMyhpmwCl>!q=W$063ZimrCP+~w2CKw z7E#>JWzD2EM1eU3FJ2C}Y(I~^BU{$U^|tkVa 9p;7u~~(Mv IAqENn8#y%js|>pG}SZ@&4Zajz&JiZ(um6V}IU_E-k96Xr}6> z3X|n@+H$V7@oslofA>{OkPcryc;)>$1;e!Aw*e2-vUPdPrf8kHmM!`Z_#+b0>zh_s zWtaiM8u0ndXk-0RcRc!LZYwIwW5K$kjddnERspfT$A||5F=^U>V^EhrUak5@2o0%S zzrvjvNo`t&kf2#eZ&tfv1dJ59(1pPy=9;4(Iqh{~lRZv_Th_hzdVlyM$i;gc4^M>4 z+hu(-q(rgvsldAF8z9OTy7ifz9loFn0Y>VGhrdR^U`W3iZX`0g^HBTSX?On5STi-1 z#zfZH&svR!80Hn5Q;%cdy83aIfG)nw`$>@U*ZK9TuwK2*A4G2=R;Tx}xhTU{shs2% ze-p3KWKho;jv#N)13afsi8|5bipvjFm*J!|HpQ2J5g4QlyciHl@Lkvsl$p_=rcoio zuRDq*x8YU!-FA> <1nw|i)lx3=UPbbohlkv!D>c`bxKd|IYoLyP(>ky) z6+-#81_h^z-D)xXrolk`&AiO;19FLK@fDEg#mFO1VU9hE3q6mv@H%fhHA7AB1=5RS z1IF6=V;K47WN5nNXV2xh4({%g>V$aRximOwlnSpu&JlR1d2Aq?426mqiq)OsWdwCk zo%18fd{f(4ZL~v~sPup(mM~q!KHIAcyp>r1?@+aEo+lCmf4 x zxg*YJOT|rXYZq$|(K0b<6ePuNqkB({?Pe&n@#vdb8K`N#z8MffM@scBDhzYB=G`x5 z=mBY$$beX$d2325+N(Wf)#O2foC; %M@d4F7Usng #t z8U(SFk9vt53Lfa6zM`w)M(9KCgh!*icIz<K)%Vmt(6%obI$JWvgQutga zRXo$`*PU7=Xf&Ejf?~6Eq@6=uv73;~bM54*k=7|&es+?Py&QJ8DP$q-vnEJ7tW!It zbnwzusgv3_z~pbkTYal8LF1+TUY;vXQkY6&iC{I|Ojm>G#aB&`7r)N^$jtS^%IoOE zq6)Goh9y?PE=R0=LJV~sK@1H;o9S@?zfUQzmMA~Rp!k$A{Ox=&S{JmU;nA&8ofNZ* zgx Gm+6BgBd!;Et>CDjru<<1m`T1{9f{1z=aphJ{(u zckV*yRjP%YcEh`oE8`)e8uk9M$ 4c{=%voM#au)mAx`Z&`|U*stcbY$!yz0_{W*3edZwhBc-~d-B-rsPuosGrK{`Z# zkk{6oG7AEZ5+kA#3F%MA${<|i>zGNbqY2!6TiUlyqKsl6FJkE@5!BgWhgLhVuNPNr zAk=$SUoAi(RoS7Mx fy4PkY254^!+c06Z9v? S?=I_S>!rV7Cq5lzmbO2w%0 z2XIqsFI+{~$;rnken{A1MkiV$YA)Wjq>9*4I*g9>-gb+%#>O$=G~2oEMt^3ew?qow zi)hl30)=aU<*E~@VL_xuw`GO@BsT+~ Y|rx%_02l%ztxWRC1y5i=R zHAj9<6n?BoS^GQe1^72Odi^Lx;<)^=E$?-NrvC#lw&TR^DCPgNp=$u<6zTm|PyA~5 zXHdGXH{25{K&R`@{j*P$IR@aG(AmdhLiw?XHNQRo>4tS>{U}W6H^lDXt`--;=U a?_?ea$^ZIL4!xQrRB}HRr)7f zf^|Il=jyVNJeGt590PaKwXRmy5D|PnDV*%wQV2eieXtIHJ1QKRDAxG={C2I;+wVpm zTD3EMjX2X(*OzOhH*$>UL!A_?L&DW2<1}wq?rd58$>CT=Kn~K~D*O1L4-+XXzQk~K zZCsk{S#CO+^s3{ELCbJ%iGNU4sist3(FBW|IpvD!=}Tk9L_rtggL<5bvLZ6Wn&bWI zvkZy}iDzBf)p)}pgA12PR~FQ889`m!QXHag`gh-TJxg=RaG8AubrCmHeSUFhv1ldb zdQa0n7-onaJkB`CwsU7aFK9YlY}7gm^=w^~EGlGtBPm&-%w3ExgHkUb_H@vLIwhrC z++LUCB%-$a*02&OI)*ttGyAi))FpwctDZawaUWzb>O=2GWlaX_F{ZmjPNJfk(y`5t zT!{zb>@qU7Db-%L9B@hJ<7$+R9C687!Me!YrGYuJ!OrvY2-UL3LNv+X(4OTF?RCV2 z)S?Ca5VT1ccc@(=Ki#FDHqX=N@5?Z}?CBJye+NmlMfy(rNfudDe3Y?2glsAFt~++> z6#bSjw$<9TEQ^uu)aokoa#hnc5Np^l8}3q;K(OT2XU^+Z@V1MM5?8s}&5+$?MPk$$ zvBnD5q0qR7e(Taud@dY -zhj!5w!WyCo$QZOtB%#|QJAM;ws>S2&bSnxmCCz6 zpE)|?!MCAD+^JugD4(FXbWJqOA1I0m(%9}?bdvs)WeUqy$udW62YX!!oNO0hpGnQ{ z!SwpBx+s1pWp}ynLym^pFAc;ZJ98t(opO30K6b5??Lm6o>T$AGX0;Jfx)m!eX=rj? zr|wJ<>DHOZiJXJX$c6rdSds&L{B|{rVvS|6X+4Ruc6t7nX;BKzLPe{y9K`xDWn%}G zndmqBN2oNis(yTXH-$S_YP>;2VLsiSZ$q@zbPO6(8@V|!+@;szBL4wjJ~_;LJDv1C z2u8Y0=)wq1_5EmxYoo20PeJi;>~QE3Nc3uJbFH@@hMm%aty0UAXRg57`}CUJ(z>gs z{m(NO8JRxVCos8p%9(-op)6FA4-31-e{h3x%xP&S)tS2?WMt35@tXtF6aAi16DbbS z)!35-3w~^e6fe1>Jr)sprS%GJMn1*E^iH6mOnRVVx{;gyTZC&HaSp=2mP|hBq*_zm z*rGV^mfA(lVMrKh6*AVw*g zd$auQ8~od|`XSS1ewU%{J99$=QlAZye$+mW~;3Bg$z->j&XyMO}d~&2fn)B{gqS zaSuh580ShCO7t>jP*J4{UUjVQ2#nacu=q-F$zWq@aIeJ~ 2(TMoBf_ z+3C4Jw@O2}g+s#TJ miG?i?Fx=K7Usc*H>SU>)i`W`f1I6n3EZbVK6 zq^ekz*nZJVG_K*S{w4U&q*7zDioV=Y4GkY@2-U~hI?@6PjCU-orsnb+6%1a3F|3ThjEr8PR|Z6& z&(Aq;1hWQSq&(sYDa|5#CBQ)|^7XcX%dFOjlaAs2`5#zfjdt2jmXi*_q~gO!9*VxN zn(xczkwa+A7uS7Q?8moIAwvzgoPuzr8jl6b;FYc@{9(BAr5UELjZG;bK`d`Ouec)I z%Gi?5ui$p8PgYFr#-0@AvcBVx^qi>|yzPn`i;Mea&}b5;oHxjynH51}Bml;bXq#Hx zT-QuO+gnS`xXU*9TF2<@_OUQEjCj>s+&~iE45NB8k!<{|LTZvFagsk92k`ms?(S2D zi?58T!hOtI8PCsZ)6lbufn5T{fJI35xgUwcWG6OpyPlB8wI|A=Lqdnf#o`*yJB$?S znM2jmJpeu)Hs@e@( `4P4!F-rw{s~$~|H0;YuS*@AFL|ARb$X55P zqt<29(Ay2h)->|Cfflj_;i-KOJ6=`;13s9Nase->XGm~PO>S+BMV&?snRzDaqVw`7 z33I{WaZau |QqroUdE3IHL0z2H}Oh=09* zEC!JH*B>g^0ESsH#xSr>|6qerJ0K^w2`c?4%SGbt`Fc?y&!SgXTtTBr|L9cW3f3xi z&EvV+>hro_c%>BgQvODO_$9LIir)jymE@V7& l@?!~+h(&FDr&7Rw47>P6+e2VzrInwmmUG6Iig&@y25*n(_@epg9p(}{;$_1V$`Y!4|g6NaCE)7&3Zj9 zMy#lRDk3;aSI|RZ>s^pDo$JD?u7CeDzL`z+D{F-xZ_10Z;lqo>XDWlain9D}lYrU| z(74i!pu(jA>DPEhz# V zdPlC4wU(hLTX!8Gn~5NL`~bRUJSa0DShq0^Is}R>Kh)9qrCP ;>ynAs}rvI%q%xy0hH7p*9)kv)%% zluhW!)3zG8CnVIXUoQNRH(Te2*cPi0iZTcbB-%vsX9RHCiKmtGX?8%fuKZ$+jYoSq zq@h@q6z`&&fF0I^mA_m!0+_4WbAI|C5f{Bctl8c1Cpwx;*7;OpvB5o&u@4l9nY;eH zR%$&U8vRH>fS6-DCqcaY>YGhP)fgE;f!CX!6!$f)pu@>V4m%*{TjR FI;r}`Yo{2ht>#VVcPYmN+W)yeI-tl&~#$-jiB0g#W zuv%rlQ3C%9<8Dzle>;lJrddT(3lHca#{j*v5mXL}nQ1g!vAxoF8YctII3H@dKzsg! z9jdAdw1i=_mfPg~E<+N^0~qw0jKXNst0r4hEs${h@;qKcNy^dxrGM{n5eVaZWunpT zK|m>t6uI!d5i!k$MWwQWBK9H-D(9rQ?)T|!yW&-UX;9C6UU<>Y^c$gL%$MBl`YNT8 z3D~vn{By;|clz7e>4me>YaSd2QS* lXZJKOJxQCGEK S2&^)B90{dYW8W(usH)sCuR1Lyqn0TT6@nm;@_pyFGW5j2pF F{CCC9i)%?(5 3p-(7V`;s!$}i KCk`at=g~;%>k_mDjxz zmN2+ml~v*1PV6ZBWb}?ktl$7cK46hA&&+0cFiuvO{U{
ZEBA?Q^!B18hPKs^#y9(}g9PXOO0?4h z0J1#FSbIN6y&Ye3qI9jWuGB$z?bH|O{Jj7=-+6V*i886nYfp3}A2A0$LVR2|)iLnL zkHehCyw+d=kQ_rbx^c7*L`m~X+5AA5DWUtZB|y#zM~Umfo)ZDLon$Vl#oLxIwQRBQ z3~=yTbYj(6DRg@sK{}bow|w%0h +n|2@DeH)+G zNMu!3-4aawIoN72nq`b%6zXmv=Fr}##BS5MQh~$gxQW=QZKnE^HRuhxSHsd(i4v_` zoGlX8Q~+CnWNmpY1NL9kvt-6o7^MPG$6({ot$R= }PZ&B-i{hCLYd_rXtWp1=xoRZC zV?jkzneA|ZT^=K kb+~cjFjsq1CJ(u{ zUD>Do$0diG&cvI3Os`5ZAst77iD7Yi8i>8uMxB5o5+c3q2ECefS{XEE?GK+VBfKZd zGWpMz;SPbYK<>mF!A=a8MUY3qp1lQ2i{jZgFfKcRUQY6O>ixZEI}``cWhA1sd@ZyN z44XXx69 X2tsIzNg%Bw_UNSNjG-PdCm`2-BaOJX}{yUf%lIpNL+pruyoum8Sc`bdo)2R+Hz0 zNuFB#6+REjd4Y!LAS8;>UXu^{?e2PTZsL35^r!Q)^}h8&7)#b|XmnOjw`4Kb_r{IR z7nH@%OaX*}#u)~=F|u{k>U#2>+~B55YH=7|-8hMGJ)SdBfhj0~Z{2T>$9yu3SVM*2 z_q|W-nl4KmD`)JEn64kvS8o>|e2=yI4{pWrc!rwofn$EA)Ke;y)8r!p5h7} ?6wNZ5)o6V zu#Y3L1L~<&a=}?a-MR{GN;>|RVih#?uQ&T5Tw*}*S$-bEwQ)fP+xj)I(6p|^t9FxR zsUdcg@Kk$pP6OBgOotL)rFU8MEot-A@a1Cbc&hC02Y2ZWk^B#IH5PSePnY+>xCq)& z(N}bsnW#8S7k&zO#WP)DASr?)I{2Xstw@y#Eu#c47V u-0Ja6QPVD&10rY$Xc72d@Tz0Ri%^ zXb~W9Vl;Wn8OBDGyne42^=xa@p5Lu+I!f}GOsN3xhKmcY+w>}Pv!I_`s5d?Crdvh$ zRm8E)B7&d6Wtb@d)FPfDzaiz`v0DPv{l|xdde;HR|Mj9A`+vYDSDO0zkQe;{E1YgZ z+h%PW@EK B&_6DRL_AJ2Q1FZGK_X_^%5Z8>z5r zB;an}G7CPTESw%3)!1&*gfVxSaWfAasVc|MM#L;h+wKrOAt3K!xd2kNm}uD>$pOHf zUa}j|(IKDdXaV5<2Xyq%b3YY|rI9j139#wfT7x={kxQ>&ljFAvG-4V! 5bE)72>@6r;0d+20KL&u=e?a$-JAUnGe5< z`Z(q^(}wR~8lT0`ejWjV-P^9RX}={tDr#%D;^E5D$nbocRg*)%t5qJeqL1lVY3H;% zggTbz3K*}~+}G_{Q9J1$aVP-^>{3+54LJq7oIQ>?zqE***85IUG9ng%2DLL(?Yi4I z@%j~L^Sk0Q+mME`fwrkqWct@xW^g<4ejd>Mp1N*+Rmm#DUm$o#A%Nf$8Jfk_E9*vR z9OJ_- zm0niQKd(d2Z*+Z~lOhhvgFJPXDmC@$^T js57t{l_04#z^LJsgF5EY} z$TOp9Mc&E5-|yPRRxC-Xz>HQJRQF*oeRbSgE&va<999tth_0g-(p7cUOz9=qz*Yw> zxC#})V?s?6iY;}}eof-~i`J_{OvdImizuu$arK#Hb9EEO4zV{8b??KNQ}lP@dhY-d zFDtI_K*(@K%No*y e5QVU%D0$kX8|YEFz)cquozA9pJ+ zL&_OBIcDDg7m^QYzD3UA5qDC@Zz0m2b{F+6cdAvd_`*$XCTa9)Rf3jxW)|DrDXdP7 z_bG;yR!fh{8Z%!nhxECx8kcc!e5uyI`m6Y1C%ad)mdZi U}-p83+lHS;W$d#&DB2T;`0x%V6(3b*3o8rs#cl)ZJ=UZ|h0 zLT)b6#$#W*eTjLnOCaXinPRs;jyEtQaT!V;H1dEsDg3VLj8o${sao5Q7mA}l&gJRV zPvTX|zMJLue5914N-%#Qt>K1B*lfd)B!#bteD MV2XPn(od>#91YbAaQ y?sV>lDi-l881Mfx7=I4H z`0~b2VB8acaeHfNpObZTtSKpMV8{pmG6_Lei<}M0DiV+kyR^`PNM TXKsM}&S XKO|+a{6g@?fqz5xeJWp-BO+7xZkE-iwh5@*NA_H=9J?oNgHRX zD@s~_%kfWBmShmux@1JmQv9QuW}a3=?IS9+O}kSgtvK(a<2YntdqLlu?u;z)5f vvVpIBOku>Gx{cJ(& 9*7Kv)<{Rts84#k3duxu0KSb<+yVU%45YNSw1r{@fu2YMb-Ye#Fb7hD$MSK@0e7c z`x=xX I!qykkD_ z7ATZf(V5j>np_l!b|DS)5X|mN#HE>WCt?^i#~NYxbV%il->G3(RAF}$+0QN)evkyM zP#)0|L_GapA|99)vW`jdlQ2G@PknPu*ugwF!-If5e6%dSYA5w>CDt{VMux?!w(K*n zo2E%t+-n`b;6hD%_@6O4CW760$dtW!G2)?xGxQTkPra1Rwja1fX>^ OE=+Ism 4%A7jFQ)^FDkP` <@}h`&f^e#jbY`>+@RKE2jb$-zUiXY%LFR?z>s9RX_Z%0D!V#)5i8H3ySMc2!yM z_B@!~`&*s+Xu`FLo|~%$M+1P )$C4YQFNMFHU*8*Uz$ot1vCou92cH@4+Q|(_ADQA9 zjJ>;(YGl2i>RZSSdkifH)S^eUP2ErL1U0APM?jQIRA}4pfDPZJ*}=J5x?8qozaa9( zZten1tNq$u6)(>1s=np #Lc7+tHlSMd;7l>bV;pgV4T?lJ z_!L)|5V0lQZGF3|vBx3IYW#j_rz?Hs;4H5g0p*O-(L!0-I310gi+9-s2AbD^rI3r7 z*ahV1q}6?6!xr?&+*mslhcGFf!({PQL4{(0?$V@J-r~7 Xm@}e8byf9r9oq57 zrA=KQ=aQl`5`qAY?$duonXSE;=spNi1LQQ4lz}Xl8k%=Mo9WjARx>jJ%1~C0E>pu} z&rs2DcuV*9=KvEdNpy+k9|Y99N2;O=pvm<%9y`2*wms60QS6~*Bab=zLLWwstGH}% z`PeLtp1LD@^gxDx^9I+sR_UJZ7kutOlj1BW8m*nmX|#w1l@FOr4l{aHKXS%_) ;dSa`A?!m~&(Ob&X(*~hj!yX-zkZJd1G$Xe#OC)?9 zwiMem25R7otYnq~t&U9Iw=jCm-T=$Or6|vAG1q5Q7CX|Es8I`vlT)#G-XeFtce=Q7 zRP_cAWNz-NodQHithMmsPm~5X_>rdi$R0z^CZI3&Sog~~qU3uWWB!x{CgDkjW{&}e zSXxqVk!3^%2LW978Qb2j5aPI9+t)Fa*}!QAtafTb5J&*@PnY1(Xn6IImH}rl2qX&o zpz3IF?gnHGuqA@>Y&mD(U(n#Vn(j_>r|R90vBO+JB{8abWrfF-&Ruk&qq)m`0skIb zYm${$)Dct|6;t1-7edW1SD=E~J;&{lm%2*P!Mgj5kw1Wf{)Z)5dD<0CPeSCWODk_u z8y_$(PQGW3r DG#Lhh>8cU6O( -vO09#E!?QkwWq*&b;dQ`iYw`3cdkPTPci=?3 zfm*7|QG2YQjJ0_A?n&|2Truh}+EsKcaGukKrPYsxai4V4MUOL^6 &LxZ>!kxrh^XKB1!`cKUQ;Hp0>a{ z$?N|VO*pIFA0x(ZFd*!I%G7Xw^X$he5l>L5f3#|TxdDO?KIB*)k#A2HloqQ9zxS3^ z!)LNWSDYEb^YhEa-%`n4_<5r}e(n(DPt&|BHFm4xOHo8)5 wl-L=U6!ZG&jYW+*tFQ0|FKQxy5cW$le3SKAa$6?q @S0^Jq*>adD0C#lb^9F^Y`Jh|{EU@ZY%u zS _0W1X%~z;L z-sq!twQ^EiQ(utwe2XX;UMua``$Rq3j~wV!U*Fc r;?npMdj6$|(q$Z^ zTIv?ODXe{A@g#~YeQo%3&W`R^Oi8H1`mcJLoa~b(KKTwh{|>tQ!M!XaU(-V+1pL6- z)+w^&PC0YkjSpz6V}cZA3?o`jeRy?u^tqoE?j2@S4Sj1%1_p!XP{uKkBU}R~E8k~P zF}v7D_RB%RX^}v31)MfDeGZ!;%6EW#}3IVoxb z8ImH5+&(njE=fDw&+d8Nnb-zM#R&Rv4@VO=+1?$8?~%gt-mbZLI<@Jr>bK~C;mA2!pLtJWD8;`6DLJp~#vhe#dPO|SZc;ek2*3j-JYqB@xD B5 zuZ9sjK|QArnp-wne)HY{VU1RUVl(%T+9jn{T{jueJgmS@X^p{m7|?nYyDw2IvA*(} z9%r-~8O5KaoTxXQ56f8oF>{GvP}0dx4eZDTiS7^xbpvRO_~bUf$T (EV&7@;P)2U>KS#;MREy9kFmbEQHd0N%Shqb#DwKlw@gHqEG@7XbQ7I-}G)H z)rJ7rUFu+-Knky?8zyuZLiFfr@nYHs;*rL6oBFT$Q$4Ek=HTepdlfQ?8-LA#Em^7_ zM6V2Rg=dn^M!>7*_Oq!8x&76Cw3si_e}L{8Ni$zDs MOL z@tqjoUDm&<3#U|4bP~7*KGBGSl$5n~XZk6_FMbv_%X%a$aHr4~?#^sY-ih9bxOKmk z=@_o{`#7#3@mK}4ihd4PuF9 7><`&eEe%T(>0fFP!I;*G&41{b>6OunBc%WO zwTWhQ(GrtB{C< hI!B=&e8%>>;e;_e5 zI5A47@SatJu9ekddXIZ^)a0 ae!PY?Ap-uuuAf;QDQUHAVjS z%16%)6+32EWA2ER)h{_eWV3lARtqJoOC9MG5hE3o!<&$0QF`*B#4SI%4!-Zp#{H-B zZv}y=phQbd!8j^%=!%}ZdFr vqR&5vRYplNM7mmm}L%k>(!k0!rmsfB)0jpM!C9}=rQytHG`m^Jp{#oHt! z-fFhLpHv%i^UBdVzCR(Gd4prj{eEzDu-&y2qZG5HG(t~afAMAQ)Dgmosu5i5z=;|o zB=<73#VMN45>o)7>8Gg?c65KEW!3(=T_wWM@cXaZj?jC>mTNT%7SHt!I24{M{cRHN zdQarmHlu|Asg5Ddah!kqGl}M|EyH5>T_W}CPO(ZjM&LX{wmFGgL!HSbbP6gISN)hP zZiNSgbP_FSk#9t38I4nQ5@aIfhPA`3^#1F5v0F6rzI*OH{=?6&&xqd96~6P@zf%_d zeYFs(!+68CO`et;B+qUAR>Ob8xpx~mqh%4|iq|4)IM r _K^@NGp!+m7^^W84e5#K!@4YF!EtF z`^q2g-Mbg4Bfq8F;_TV}U-p