From e52787bf3ffafc084b387428e25e75ebc58a847a Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Fri, 28 May 2021 15:12:28 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E4=BB=A3=E7=A0=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/tpTracerNif.so | Bin 0 -> 40904 bytes src/callgrind/tpCallGrind.erl | 2 +- .../{tpFileReader.erl => tpFReader.erl} | 3 +- src/flame/tpFlame.erl | 424 +++++++++--------- src/messages/tpMsgRS.erl | 31 +- src/messages/tpMsgSD.erl | 113 ++--- src/tracer/tpTracerConsole.erl | 7 +- 7 files changed, 295 insertions(+), 285 deletions(-) create mode 100644 priv/tpTracerNif.so rename src/fileHer/{tpFileReader.erl => tpFReader.erl} (99%) diff --git a/priv/tpTracerNif.so b/priv/tpTracerNif.so new file mode 100644 index 0000000000000000000000000000000000000000..4bacf636e7b4bac94fee4513b3fbabbe788777ba GIT binary patch literal 40904 zcmeHw3w%`7wfC7BPI8h-CX)w{@El$WDj~?jC{V&9FaZJ)p%*MVnM@|hlw^`lW+2!C z5kwgx-q4EDmU{7ZCM=16)qE)okeE+o{XZD;-{BHZZ zzwi5g_i|ve_xi83*Is+=$2ohSv(MRDzHE`*X4B;1(7vKEYCK2E1jD#pN@!Y{R;oGi zcbqns$qq`e`o)Zq9Fn2t<}~G@Q`AF$aCq2XGhAKBX&I{NSbk+f-V#2P#&H>{>4?>} zjPtwJSwd!vrWK}hT&A;wq;%aPUAIVQhRj*ui_j3s$wvAeBn2r#4@1r3YSwheL^^9Y zUZj&D{H{bg;c6lA<$pn9lsa6K{< zZe_kC9aK$=m#@~~o^G;5h%O#Y#Wu@7*>&tA~k~_E8Y9 z|9t(Pqi;`7>-MgH>Xv^!RsW5#Ti)rM|6K97XL3gDJ95!oOYR&y=Hj32-*WJ)ch>ya z``0%*zdroh;YUVmlW+R{d(V9T-q9DnHvO|7oLPB)@UWx1#XywV=;on*O+NO)ntd4cxl0T>XrW;3Ojo&x(?xBA?WMo`%BHLRwXKClu zjq!(X+}p8l_HVD>eb*ajZW#H}`uo;iRk(M3`%Nfb%HuLL2HD0`fyrnfsc^zPs-TG9 zFo2$-0dQ)7sq}w50Dj2;dVW3tUJE^WDeRm#fcz^1;CByzFCW0pTr}-e<+^eJJ+lVT zlR1F=Z3DJ0Ok7S0Qe;Xlxyq&_SX!c zr*8nfasWMxknh+O{`@AeT&+m+1~`+(LppY1U?u#31wR5F;axRM%sgN174n@H`4jM4 zp61f75Cfq+kIfbGy%s(92>qHx&mMudTkxR3k6ZLa;UCJkVJf%td*TH0ypTU=k^c_( zRIU<>{wIX}!-f5$xjOBBVgEjh{0^z#g69eSr50Qlde&I*hlKoL3w}iAYq9@zDR03q z5V*&p=T`zhWs%Pp`Wr0raUtJkk>4!j3oY_r6L_~pzDnpRv&hFByuNhmAULF-e<{{sf=@y(9S4P;&ns)Q{*ydg?VOPBw8+muf2Vf-6S|ndaT^b`h>)+g*mIY_4GaF3 zowxrs3;u|}&sp*<5xCnTKU&}|7WtP2KG7oIBmA5#?7vso{~$s(&RFC%VSj@~|0_cN zqy>LZ;8hkqXM~<^3qDTPdkHrrmj{~W^VK&shkdP4eD6jRqsD zDi<_1hl8v9HH|^eOU>~G+Wfw{P}tuXx-O^%!=XA~lYf2C7mc+bp+DN(f>m@-`FT(jm@pWS`lxF`J$nwAgpR?;ZSX;HQ9?ocU`2pi3|3g;7`VMJw|lj0glPLK-6b(gr!xW zq65Ny7NvF^jS0;Ri3oz%5YI9eHVd0*s?MRx+Tg48NBzlc zj1DZ@)?j0uqI;uX+7oCD`l)+w;9O>2f+Q0eMR^7=_C$lIPxMPZ5UIv-u9Aleh;d6m zq^uT$Q>1xQe{Dka>&+MinCZ+JLR`&ILXDxCKyhnx@l0*;vWoc&e3uqqT0Fyy)1*4Z zfz!_4bU1K=`M>+mu5HITT+TUS!$Me&aA>;)RuBqhYgpy@E@(RBdX>CR?;6kX!(u&3 z;w=LA_&HzVk}k90kI&M-tc1s5)6 z_(=tyspvVS;BqaC*gXnP>ss>YRdBgxM!a(h{#7YVp^p>fe_A_}hoRtQ5=7`$@QDgu zsNl4ICJ&E-Pm&H>wEH8qu|97 zL|CohwALt(1_h_JMR~L+_&5n7Y*TPrPn1Wyf{UpUW9?M%Wl|Vnr-I9MPGav1s|*6Rbu?J0TV+KRA9C_ISj}Tp-oX+?*j3F5!iVrE`+K;W6NC@tQTDgH zniGT<@+kXzteO*q7x5_jd%T(xgb(9U_V;8pCkP+TqwMdgYEBS7f=Aiko@!1IK8i=# z-`;9Y5I&km+23>3oFLr8qx`RE&xDUr@$>QX9{3;Vfe&*=edgW%p5g7#C(Xs>$d2x) zz3;ep`_sl#%o}|(-UNKMZ^9oCCq^Yx>J^c-nrUCx!X}?L+z<&1hl_pIC`q|U- zOxnrlsU}SW_}SBAOqvGnv!`=Sng;B%r)?%p1NGU{AABLpO9M2@Z_+d{qx>dK12W2Q z(lijCJ^i3b(}f|*Z_+d%qx>dK12M{P(lh|0{3cBUFUoJyG~lBACQSn^%5Ty%z@9xl z)ud@)MfpvdE^ttONu#!3!?j}0=!F`qZ_t0T&~IC4tNcqA{;w?b5exm8g+64Ve`KNW zwa`DX(0eWPZVSD`LT|RvF$>*fp@SCs8VkMJLRVVoatl4jLeH?!lP&ZF3q8_854Ol$+`MKy|3$8-F{R z^?Kv0d%W?= zQ_JGzC%qk;@r#}Ec2xFwJ6894JIc>h#6PWw_x^&09&h~UVFX&wR%z%5)a`9K)$Nh&^y>D@hJ61qQ9 z^<4is5_AmC#PrJ>x04UR94VN!oY|Db)CF@fXC6*szK>Cc)Lz7y>ywzL1T&j6pD9)I z{H0*_{)HJ7N=o^dVE&pjXD6k+S1^CdnM0G9w+QBUIrE|<<`%*H7H95GVzvmTk2Bja z@lJTCS}^Bx=B%WYD+O}`XQm}F7Ye4qnO7z;O9k`HXUqxjCFMCvFrVYh^dx4MU_Qv1 zXR+*(u=b-uUMsh9=AopNrv-B(XC6#Sc|tI+=FFu@DSsiDb2#&hq?GpvriU}{Ok&uO&Q4PM8^QeI94qJ1B<53s`37h1N@D(-U_Q;6 zBa@il7tH%O)02|9i!%dB%o{|?CeC~=iPU|yBxaRh4<BSm8+c z_Hx1e^b_WUB}vRlg83?E9!X*r21~u6>|cY_7+4=_IJu+mV&%|GT{_D`{K-ewC4ll zg=>lsKO^fAVNsl!eGg|YV`<5+`dvJGFX!LD__0e8{JbQ7E$5SGDmdc|6?=vdvK0HD z|2%N9?L3K6Y_z3>SN?!iKZ{qMZiiR)#bdX^D?9sp~!ii@$ze*=<236yD%@P_ov<23u~xrZ?;PjU2<54`c?@$v^cRzE-r zxC`ffBpV!Q?&g|hgIkp_BUhL~j|#zzZV2OQimYX3yaF}C;CHc-3QJDk4avu8($_PR zL(uIy^fgReI>N*I%rP!|wC4o~#>=}P*PBZ2xRm=LlY40U<}NK-xqb6P zT68h4(2g(bs3cY7;=*MeFpEWz8P5;>v!k+?>A(w6_Eij7jm^#4>sk?|+oxfnb!fb5Qm zYC0jB#?hYeS!y!+T&(D#p5P|x{Ig}}KLD=M`FHE@qvo%X*TbIVOUuNS^e?EUQEWDf z_RRV)NeRCqM}NoFH^zv^Y0&H$N@if>MA7g=Yl>GwTmw!eq^ z!4*D}E<^liPv`?G6s0bMjYoUdN*eJ>#5+?YrwIa2Vo;Kw@+7_9qcD$r!?eMUDg*Ie z*g+xX0IUpgjKXnYGC@R|BFw0Vq!A>J9HQLwM{rLKo>wfo{yjJKtN3FECNbXlAq_!% z@8>jWi0^Iyu`Ir$g$29Y5Zurm_2f({Z_k-j*_ktG^?{s8*L3Af3U=pAYC4`XDRwGn z(&k=e$l2jVnyK9#JD9?K-X~5u$eP=FN&OBFm`8V%;4dzFw4*zwBZj_)*vuydAsAYNXK+h=$F^p*YQl z;yWgCF8Aiqp2qhH#Q?+^@n^j8mwWz1q8*3GH)#LU&Grx3|JOD1`Tt_2D1>Q$W6cyG ziL4olC)Et;`>(7S$^bP(fvlOwX%((#Hn*SG(OywiyfWDE-^lo=kKN29UX9{Ra+Kyt z{c7-se6Lbkr*VpB}XNDs0Q{s#c>sbDu&=|@unNVe{a^FP9N6}wE zI!GPuc@AbU&mn2jb?{y=lHBNl89k4C$o92lJDt6D&-oZD%RT?X>?gm?H-LRc*uAWL z40Av3rc~n+2u@PkX3Pb}osXW0e6*=W!F`C%n4{zsXJva1?i1@~M#-$;(#W z%f?1FyWlPlofBqL6|+{%T*7CaRE?TB6?vE0N*F)GUYMW_a+^ZK*rPq~o@O-&FXQ?Z zaZ!WpK7x7Y08Ti6ocF+a51jYFc@LcTzp0V=>Yo!8f%)xb_Qjyg2d^0e$6)AG3za~czX2U z*lU=nVxn^R_kDd65gtQWhS2|JUmxv)VHL9YUJeZ0zalJBjcfgy#@G zjIa=kPd~yXXDPz}K)43sJ%2=cgij&dhwx2=hY`|D@+87X5uQW%KEgsgi!vAUsZxYn z5UxS!#yUV7!XILlWFNvttlb<&7{=1pNrc58Aw9xF2n%ro`D(1ymC}u590fekY}c*S zY;A7a@T_#B6Sj^APLEl1zuMQ=3tDrz7r6?TsvzDJeT`M`-1E=dpUCEdEN^6V(`}{@mHDrhk)0D zzb1))mC1h*_FSxBtn^e--#m@AmbLR{38|q>qB%2>wZxZ-3rQe=GRo(N+dZ{}A}&zJ&iG_+!Cm zdzQ`eKW?WCSoy*KceJrwHGglyz5;4<;7=#Mte@rte>(WjgI}elw=XvBr}h6n@aL-h z4T$o_}G&qQBfds|KZ&rJS{;6H%AVMjU5 z`nP{->i-D*ThM2^lk$JqTOB<-HcDr z`Qy9?&U@gz2hMxof1U^AzDK#wkxo%`&|XS9%)J>NMrv|ThPRZ{c$SJCaz7mlKP+GTJ5-q6bLpw0!;^=jA2KqFjV3P*~M1E&Q4*7-18#y!*VjSzfH*Fh7O~pA8}e{ z^wW7fAFSy!TD>+ECY%rc8+bsz$Ie=m5)VhpL|7%lY7w@Guw8_mB0M0%E)jN%@VE$1 ziLh6MaubewjK&U+2unm*Cc-KaR*SGjgzX~i6yX68c8RcCgvUjAN`$>4O!;yoZVI=@ zuMjDsa{sj1k1aBCAN4?dxu1F<{+si-!h!g5fAv6oxzAdTQx@~+J$h^py4-gy$1l0x zTJEow>Eym@xsO`zo3;r7+!6* z(&;5RgKWlgNM!s7HRJfk&;|$&GIUxtFverhc5EHGfgpQ&DJbM{9KwU_x=v|I2-;V) z4Fbk)ihp-F#WM_pUIV3{5Zcl&rhE*28kA=KBNCoWn`#7sIeQ_UHZ7e^(4Eh~g0z_# zG#fRXucINPUB(Ev^S6*qD`iBX^Ai%goDm*pFSMmy!H9{@r%2l@MwB=Yz@D_(j3{-k zAUoT)ay||Jq|Il<8s|(R77p10fokVc(zdAJ1`rL-2Puu0 zS>58imN*rRh&s2EIZK>BM0Rb?HsVy~?FG^9{1%mGdCqAZ=Ewb?&~K&SIPvfyh&?*(U{b24$(6?K6);Cz!5T*J&c=)8zjUz_nHurB96 z5aG*w8pL7eVsceAv#r~CJDF2s{1ghF){O5!U3z-a>yXQuK-w;LWj_jWhp`C)4ucpj zYDi>P4z&tGzX2lqG|X}s2Pk?Gv^b3W5oNN^NZEYNO$%8@-1Pzi2div1z52@7#Sr%! z$ja#8@dk)w49lWM;K~D@j$f{(f6h|Q1FB~MaW5mW;lqe)+=V}x{=soDjnDBXGgvSV zkUj-}M9a@?a@B)Me-FvD^wlubmOci6J^epOv1U95Pi4l&wF1lj3D}t(06du+-H!lq z))2BOmvYveR0EmUv7Tf&FQatdEciV*Zs!M-`L`ISP|Kt2e~n*0`$GlF|1BwS7-WjW zn8p5*ej^NLWNx;%A)dJpc~xh&yAPsaI*SR}mchG31?9D!5reo%H!zb7XCC#X9gO34 zt{`J?%%${&&ezDKo3efd!sGlM5jz<%(Yb=O#j`#Dr^Fc}Vpmo=f>P%uvSv3^UFIB1 z+B#X8yw01b=-VfX;4vY}RuNJq ze+^avS;G005a~YwYCK40-fsT@DhC^x-5DjIhtMl}GM{AbhRBN~@|2?p!B9TxWx8yx zH6YV(fWfx(SMb-a6%IyG?2Bz=U54X$4P^pH)NnMA#+UipT&!V@aGQ`+!=idIVv3ml3KZBllyTH+EZJ5nv1A<-YHvd5%` z6RPZCDN8S{GfUhe(Fv;TDXC$$T0@5=O0V@aHFQf2_p09Ml(N58b6X>2(^RiFNVHOw zZIK#!RMahHX_?ftp;yZ8R`ni`=(VbbZi&)d%+ydQ_3l+=4@i`TdQ-MjqO>GxqWdJe zO|{{i%x#@&?>>ndYQ0UA8dj=kn?%p3UhfeaTr}*IV7|!CO1f6GlxpEG`5r(S4tYH| z>|Qj|>^A7q1;W6!~@MV~;>{@`#L zjvRZswvLLnf*eDneNT1K95VmG37mU)YtbUo{@^8?T{fzzwI1X&POd4so^<^rhXzOc z9`C4dv_Z4~Tb`epvVZHSM!am!{tz3)?R%<zmFB(zNdkg{XQFMu~-M(jyHtY+^;7+E``d&)g{;{}j z=FYq`S>K(>`tCIK4S#@IRwvVEeGjE=|MjN6PN8oNx35#^TgC0`6#BeHUh>DcSv@N{U=QIyOPb_m2B>=gt>Fc+z``eeZ8k`{|c-G@#2SsJ});nB=jxh z=7yL)#~!o=){@o>d#j4>C-1Ij4GG@nZJ|-Lg=%dyo#^d>Y&j7U>nZ32H=8*JMzb*s z6?q>MzUWC-k=2@qE*b3W*tMCQQBft+VcUPkRIn~t!Mcm-><(X!q~#?0x;dO&Gx7^+ z)$7*sW?Q9=p=%ZUYNlRJ;MmPPrhZno^vvoJN%pOtDAhB4tII?;(8dOkt?gyocmmc< zAS*SEuKnq-?SB>u$5FS1NSV+$E;v=QowOBl71iT{muj}(N^;G(;55zliX^MX1*dDa z-$~LtE;vK8y(-DFalx6I?KMf_*4#?6=2zSrhp>j->txn=G0)@H{3_X+=cEd5&GVAv z)*P24x8{T-xiv3HvP>IKuimyj&9j$t$0f`aV>>tm9{ z<-m;?O9yFQv_vs!J&Mt;7MWRRyB|;a%P$)W~PO;))5OfQ z9s+4%W=ipNBTdXqxi)oXnzjRFNSiqPdB_>749rpy$>_l!`ejlAT%JD$2EM&O*A7{? zBA%`-B0OuC86TAOODIa$?gMOOJ!Zz;S~i>e8dHr2-~<=VeGM`9bc+}B0z-w<d?n`!wigy@?LAd$cmrnI=KKDI~ zD3i^G0w$ZUrQM85&zxRJb&^Z-;LNyd0@F_wpG))L%v}u0rFn2>2anS{*vaR?8A0Gz zLBJ`Jdwql*t;+oom}xBlvNz>WedMz1_WZ$=NiLgv6|i`|+dkC30x4YcAdL7P z#5I?P6SU%GXxYV}CfbL%iRaScPW#ZY7c;ptBv+JujBIdyM8q(IvMY4`l8E7XKY*AA z1)(e)1}ShD>G+#QI_)DIglE42W8L;q883ry)e&P<2DgJ&=|FHh(n!ueYW9yImhZ89 zGRDAF1yOj1s@sG z$;bvMpN5#jxEpE6Fm=0&Cm;-;ior=dj;3o)+Dt>U)Ih2CXPYW_{a5>2I8g zKR9mU0nQ-Pk#tyd&DS9zO!E$97sa<`{}W0n)$L8T^fqB>l{Wl-vaUV*8En<2qnrJu zbJI(tnW<{Oh^z7rqe8dOL5RdOmgh$-8{?>s%A2z9edu}Ja(wR1y@^{G&12jf6LI2X1^wl zb8px(l64m!>n@nTY#B;pg1wG)EXSVCExcXTv)Xd(*|$Y2bW;&4m~qyx7TESv$LGdX zBpFvRgPbUht5}|F+|nfDmI>o-7RD_X#z|vVCyXh2m>f5U4Jf}6)4OkThr!=nWDNOx z&Sgpdp0k4Yl4@=Ey;PX#*|&?d>=7pB<<}`KYhKeYmwl>eDoIZoLG)sgqRPx|7MN5I zlV;rq49$KBbM2(%)N(N28pb?3$wxyLW@p^77n>b++^tA&J7gPAwJtB0L=!?;#qh$`}_9$5O9wX8RhzUWKp zZJ4$3psiV(hEOxYiOR`KCufaROUYUo=b>fEj^g1RXH23ijYZ9C;|8qy@zz$N{Rx+x z4$X`6na%bW_}_+unh5?+iM2+i;LYb#f{{jlxE_Ic5_?K09B7Qy1~n!jh$(HQGkr6s zPsXG1v9`(e;n$bhc~kA1OAqfuPzo2v=%pz z?eY!#k>UUf!=!2>8%UD#nJPq4_||A`ZIECL(Rd_2hVL0Hs#sPI07HBt2C+~xK%6F8 zH?{hLkw_!Hl)zO8BS4Vrbeoo8my>eGF^5ghGJd4n?3d)vMC@(EF3Wdh6pQ%G{6Tid z$KNW}bt9%{*+0)H&~0m{>Up5tQ9Ww|Q6J7T>hw&9^DX^?JM_$Lx?vRS?xnhZM$fbt z4+dFBWaeX%k@a)fU>{`rr?j`yHX@%A-S$sYA(Zh)eZn1j23L@^RL?NB=`IQ$(A^}z zRL`QoxLj*`wz= za*Vh1$#>|v+l;k51ovt^pK`C$bL#cE+w`1m`lYk!Z?^ryytNEmqJkIXTd}$J3knhv zy74ir{ntbxuG?nMK=Yo5dUj|rYT*Bk8a(ZRXb3i9J4x%`+T?5fk$N;;xMg-tEYujC z914GhI?aDYXHd;*Af>!_$V(x|!>o7A(QU@F=mx#$22-iwpL|r$dQ{IqGhRAX&-qZd zqd9EH-|f2X(RsSvnDdc+e!+)&#z)k!?d$UNi;VGr59k^8$@#kd4m}+$$uULGrogx( z?E&4HW-raths@IL)W)Lvh*$JW>=O#m_C3gajIrbI(lYuLeT0z@O|ui4ZX)}1<0BpU zXBa=w(YZ3drl&uk+hdei>?V`W5KZdE4|MOLYg@_z-j_$2McIZbzqmbnaO_|82csk8bSXMg-Zw zV6S8zU29yUU-F@zeNuNmqGxZXzZ16Wt~+%5cKf(t@Zk8t%9ICU`#Q-@}cgc zS|I=1@tF#n@--B;d&k$>&>V@@#Ol=V#rP^#uEDcMo69F{Dfx6J3Aca@jxOWGHv)D{064NhgWBaIx;>ffN0 zpHQi_inLwIgaY{DQD8$6u1%;>Dzo2aKKfFPhy96#>2ou|S`R(egm2vV zn&{(7>gkkaSA*~H#9Dkc{?;Jlg>kMG#j<{Gh(4LqT<7EdSs83?jzt2&Rk%#3pl{*8 zPti>p|11zbe1$t@%}Eyqlv=b+^5gQW$`>#L*ypcU_81uhrbmcPR{>$A`h3Yd+Ne-` z8=`dMYD&dEZ^es{*ki|=rTH-_b%P&ced$g<7`A~cl$!~$uaGAb-)G`JSzSR51Xqbb`)X6iadnVU=Cf8L}EWlrX>!xs^A%d@Y#abuF!W%>3+R0Jcu-FPF`z+LC zbew1~5N(c3rY{PiK}$xU5#MT(Y<%ntj6j`2C&m#P9P3O+%YC10KdKgMsgL+^J?-Nc})zqK-58}W@S z{@o~%81=^UMrWc+1m0?7pQ)ri6ZSWWkA}&1#CM*eCGn3~`KW-x2V^i^s&i-Hdt8xC z(i?I^EprprTSBP5CP;u!IkS&unFCXx!7n-jjmYeybbNPkqTy2wQZ*oDo%psIKNpE_ zm?d`qD%}nh3rmqDcbSi!pMdN^w}^i z`i%x3T0Zw-if?vBzY#>^3vSTZMqDLZd@r!oUmt{mpf8M}QZ&X@)ar2N$K@wZG8_E- ztV_Ang(4ywVK|09VG0uY(4}M}s5fKWQdSV3T9O8TxVDjcnaIEw_2a955uDf+3t3m= zuAs7NwB?$8IF1@Aea}$a(2R`9DA67h61BwP!0pS9Cc#gi(Mk!N$r%cf( zn=nD@htcp^$Z#wm3u7HHFHMyv`y3nwd4A&eqmyg+gkYquv3a8pj=&GdmmTztvV)j4 zTMq}O(#KNb4s)N1n#O_IwcMYQjO*q;mSj9lllwuE@pMh@BT2>$P3}iY#xpdzZzUPW zqc0v6;cQi<^G~%e6W^kKZXNeW|7|?OP0qXbhV1`xC8gaJSm!z17BvTu1O_N z53uz=97$hhN$?$P7jc4?F^48^6G?=Jj-)TPBzTUbFSc;phG3BPL0>|O$M8rDT6zC^ z1j6tVOZ|=!c#8$U47g441La4@BK&MA>V@uUQNCRkJ)4+(|N7krJeB?T2t9IbNjq6nFMfo}=k}@z#PJvW zQk9E6jtl?DRRwv2Wf&YwezV@znI>@S4Wgw2x88L6n!v3$|L8ey(r>-V*)H&Yw^awQ zpWfAA!{Y#5)Br3;s`B2!^!Fb(?qPcR_qzuf-oIZz0vtDE`i&>Q5_+ut^tTM}-`_Ls z7fnEx4njIk@9T<-)X_O0Z*mpW+C5ck^ip1<@2*r|NR0#V39v0@Pih- zTi{(5{NI76vWFf!N`=#{04ncci=NL0kk_$fm`V@5dx7+HTlCBXo~Krbh_4j#$1L*e z2GHLq9#55d(wj6D)3VloF0my_GZ1~eMrdnSmciYPgP!e+>r9^waC*`A*tlw z7xL#U@}CJ@3naHUdcq}@o{n5_U{7j zNm1|L74q_Fb*cXzfqN`^4hvj9SuW*|2)x81|03{I<$4D=8)v3*qvT^49}7LD7Co7` z=R@+==cRF5FQtBZDv116X3;Z4;9d(pSKw6^{3?O3vEY8-WRLZM<3`}A%DZ&{JU#$U z-}@!~*5|o@0-VZKZL#NBfj3z2mj=-PuF!9NZ0QSuTc7);tvQr$i$(uv;Hk=644mw@ zK5?~3=xMX)St;;#3mz2sP7D4`fp=Q)J-}1hb3n-Nv&jEg;0G-D&w;1vFTWA;2QBh% z0#B9ipM`vvMLq-1*QAmk4xIdE-4sDjkiW8! zyAlN=OswEw9Xi?^4q-hvB$q(tYB9@~*C>ky8iW1_8md^yrHp*hW?#4&H)`6@D{#|^ zy@3ylZg4bP%EofzdS4x0pI25UKV?6iyMV0_GPO;#1jtuenXUcq=`b{DVUi*8UIg9i zQd(Ak77CewvW(0!qBf|QA7}{H#v0L;u^Pzut-(fdN0F~|G9_}onn{FOWdX6C7TOTx z>1fr_$Co7)fbKr|8##bQWhx*B2zr8#mzo+D-K7xjSkGbPfaU9OGz4MJQSOKM>apHS zj*Udo^qG15C5tR7SFXh!5QYt~Sj;S7W{btZWCu2itj$)=m!*|kd#sW1aVzn#gn$fk$e0|{4d^y4ldHN;d4tSp&WaA)!4H;pn#R-k$7fua3g zoXK)z-7?XbEjRk)OE=j-Bpuy<7!of%WeUZhE1EIO*>a;UM z{-zL=K{aWHl}t|!hSg$RR?z(G5x5NG{7KTqh^Xo1dcF+DOF&iXUf#cup?sf~ z%->r6>qYwcLZQ6RA;aTBF{Q^UUO)eJ03*L*+Q1J_a!BnE(SG8J;5L$|2Lq@NV$b{LT9{<9Q-)fsz^ zwf>G+(#!ivGK^B^QV!YvBt*L_>0XEwlkd0dTE-=;@{*R}?;w_=q?h-n4*w_9zl*fg zc4hwZK32C#-z^+Q{f`crudGLUn-7V|^zwdJ`*M=jw04WbWqKL*LPkxmRZ}V)RXi~M z59vRJn3`VRKl5D4)0bJ&dpK {ok, OutDevice} = file:open(Output, [write]), State = #state{input = Input, output = Output, output_device = OutDevice, opts = Opts}, write_header(State), - {ok, FinalState} = tpFileReader:fold(fun handle_event/2, State, Input), + {ok, FinalState} = tpFReader:fold(fun handle_event/2, State, Input), flush(FinalState), _ = file:close(OutDevice), ok. diff --git a/src/fileHer/tpFileReader.erl b/src/fileHer/tpFReader.erl similarity index 99% rename from src/fileHer/tpFileReader.erl rename to src/fileHer/tpFReader.erl index efb3a52..4ac853c 100644 --- a/src/fileHer/tpFileReader.erl +++ b/src/fileHer/tpFReader.erl @@ -1,4 +1,4 @@ --module(tpFileReader). +-module(tpFReader). -export([ fold/3 @@ -17,7 +17,6 @@ }). %% High level API. - fold(Fun, Acc, Filename) -> {ok, IoDevice} = open(Filename), Ctx = lz4f:create_decompression_context(), diff --git a/src/flame/tpFlame.erl b/src/flame/tpFlame.erl index 0830b71..f44daaf 100644 --- a/src/flame/tpFlame.erl +++ b/src/flame/tpFlame.erl @@ -1,197 +1,173 @@ -module(tpFlame). --export([profile/2]). --export([profile_many/2]). +-export([ + pfs/2 %% 分析单个文件 + , pfm/2 %% 分析多个文件 +]). -record(state, { - output_path = "", - pid, - last_ts, - count = 0, - acc = []}). % per-process state + outputPath = "", + pid, + lastTs, + count = 0, + acc = [] +}). --spec profile(file:filename_all(), file:filename_all()) -> ok. -profile(Input, Output) -> - InitialState = exp1_init(Output), - {ok, FinalState} = tpFileReader:fold(fun handle_event/2, InitialState, Input), - flush(FinalState). +-spec pfs(file:filename_all(), file:filename_all()) -> ok. +pfs(InputFile, OutputPath) -> + {ok, FinalState} = tpFReader:fold(fun handleEvent/2, #state{outputPath = OutputPath}, InputFile), + flush(FinalState). --spec profile_many(file:filename(), file:filename()) -> ok. -profile_many(Wildcard, Output) -> - InitialState = exp1_init(Output), - Files = filelib:wildcard(Wildcard), - FinalState = lists:foldl(fun(Input, State0) -> - case tpFileReader:fold(fun handle_event/2, State0, Input) of - {ok, State} -> - State; - {error, Reason, HumanReadable} -> - io:format("Error ~p while reading ~s:~n~s~n", - [Reason, Input, HumanReadable]), - State0 - end - end, InitialState, Files), - flush(FinalState). +-spec pfm(file:filename(), file:filename()) -> ok. +pfm(InputFiles, OutputPath) -> + PfFiles = filelib:wildcard(InputFiles), + doPfm(PfFiles, #state{outputPath = OutputPath}). -flush(#state{output_path = OutputPath}) -> - PidStates = get(), - {ok, FH} = file:open(OutputPath, [write, raw, binary, delayed_write]), - io:format("\n\nWriting to ~s for ~w processes... ", [OutputPath, length(PidStates)]), - _ = [ - [begin - Pid_str0 = lists:flatten(io_lib:format("~w", [Pid])), - Size = length(Pid_str0), - Pid_str = [$(, lists:sublist(Pid_str0, 2, Size - 2), $)], - Time_str = integer_to_list(Time), - file:write(FH, [Pid_str, $;, intersperse($;, lists:reverse(Stack)), 32, Time_str, 10]) - end || {Stack, Time} <- Acc] - || {Pid, #state{acc = Acc} = _S} <- PidStates], - _ = file:close(FH), - io:format("finished!\n"), - ok. +doPfm([], State) -> + flush(State); +doPfm([InputFile | PfFiles], State) -> + {ok, NewState} = tpFReader:fold(fun handleEvent/2, State, InputFile), + doPfm(PfFiles, NewState). -handle_event({Type, Pid, Ts, Arg}, State) -> - exp1({trace_ts, Pid, Type, Arg, Ts}, State); -handle_event({Type, Pid, Ts, Arg, ExtraOrMspec}, State) -> - exp1({trace_ts, Pid, Type, Arg, ExtraOrMspec, Ts}, State); -handle_event({Type, Pid, Ts, Arg, Extra, Mspec}, State) -> - exp1({trace_ts, Pid, Type, Arg, Extra, Mspec, Ts}, State). +handleEvent({Type, Pid, Ts, Arg}, State) -> + doExp({trace_ts, Pid, Type, Arg, Ts}, State); +handleEvent({Type, Pid, Ts, Arg, ExtraOrMspec}, State) -> + doExp({trace_ts, Pid, Type, Arg, ExtraOrMspec, Ts}, State); +handleEvent({Type, Pid, Ts, Arg, Extra, Mspec}, State) -> + doExp({trace_ts, Pid, Type, Arg, Extra, Mspec, Ts}, State). -%% Below is Scott L. Fritchie's ISC licensed work with only a handful changes. +doExp(T, #state{outputPath = OutputPath} = State) -> + trace_ts = element(1, T), + Pid = element(2, T), + PidState = + case erlang:get(Pid) of + undefined -> + io:format("~p ", [Pid]), + #state{outputPath = OutputPath}; + SomeState -> + SomeState + end, + NewPidState = doExpInner(T, PidState), + erlang:put(Pid, NewPidState), + State. -exp1_init(OutputPath) -> - #state{output_path = OutputPath}. - -exp1(T, #state{output_path = OutputPath} = S) -> - trace_ts = element(1, T), - Pid = element(2, T), - PidState = case erlang:get(Pid) of - undefined -> - io:format("~p ", [Pid]), - #state{output_path = OutputPath}; - SomeState -> - SomeState - end, - NewPidState = exp1_inner(T, PidState), - erlang:put(Pid, NewPidState), - S. - -exp1_inner({trace_ts, _Pid, InOut, _MFA, _TS}, #state{last_ts = undefined} = S) - when InOut == in; InOut == out -> - %% in & out, without call context, don't help us - S; -exp1_inner({trace_ts, _Pid, Return, _MFA, _TS}, #state{last_ts = undefined} = S) - when Return == return_from; Return == return_to -> - %% return_from and return_to, without call context, don't help us - S; -exp1_inner({trace_ts, Pid, call, MFA, BIN, TS}, - #state{last_ts = LastTS, acc = Acc, count = Count} = S) -> - try - %% Calculate time elapsed, TS-LastTs. - %% 0. If Acc is empty, then skip step #1. - %% 1. Credit elapsed time to the stack on the top of Acc. - %% 2. Push a 0 usec item with this stack onto Acc. - Stak = lists:filter(fun(<<"unknown function">>) -> false; - (_) -> true - end, stak_binify(BIN)), - Stack0 = stak_trim(Stak), - MFA_bin = mfa_binify(MFA), - Stack1 = [MFA_bin | lists:reverse(Stack0)], - Acc2 = case Acc of - [] -> - [{Stack1, 0}]; - [{LastStack, LastTime} | Tail] -> - USec = TS - LastTS, +%% in & out, without call context, don't help us +doExpInner({trace_ts, _Pid, InOut, _MFA, _TS}, #state{lastTs = undefined} = PS) when InOut == in; InOut == out -> + PS; +%% return_from and return_to, without call context, don't help us +doExpInner({trace_ts, _Pid, Return, _MFA, _TS}, #state{lastTs = undefined} = PS) when Return == return_from; Return == return_to -> + PS; +doExpInner({trace_ts, Pid, call, MFA, BIN, TS}, #state{lastTs = LastTS, acc = Acc, count = Count} = PS) -> + try + %% Calculate time elapsed, TS-LastTs. + %% 0. If Acc is empty, then skip step #1. + %% 1. Credit elapsed time to the stack on the top of Acc. + %% 2. Push a 0 usec item with this stack onto Acc. + Stak = + lists:filter( + fun(<<"unknown function">>) -> false; + (_) -> true + end, stak_binify(BIN)), + Stack0 = stak_trim(Stak), + MFA_bin = mfa_binify(MFA), + Stack1 = [MFA_bin | lists:reverse(Stack0)], + Acc2 = + case Acc of + [] -> + [{Stack1, 0}]; + [{LastStack, LastTime} | Tail] -> + USec = TS - LastTS, % io:format("Stack1: ~p ~p\n", [Stack1, USec]), - [{Stack1, 0}, - {LastStack, LastTime + USec} | Tail] - end, - %% TODO: more state tracking here. - S#state{pid = Pid, last_ts = TS, count = Count + 1, acc = Acc2} - catch XX:YY:ZZ -> - io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), - S - end; -exp1_inner({trace_ts, _Pid, return_to, MFA, TS}, #state{last_ts = LastTS, acc = Acc} = S) -> - try - %% Calculate time elapsed, TS-LastTs. - %% 1. Credit elapsed time to the stack on the top of Acc. - %% 2. Push a 0 usec item with the "best" stack onto Acc. - %% "best" = MFA exists in the middle of the stack onto Acc, - %% or else MFA exists at the top of a stack elsewhere in Acc. - [{LastStack, LastTime} | Tail] = Acc, - MFA_bin = mfa_binify(MFA), - BestStack = lists:dropwhile(fun(SomeMFA) when SomeMFA /= MFA_bin -> true; - (_) -> false - end, find_matching_stack(MFA_bin, Acc)), - USec = TS - LastTS, - Acc2 = [{BestStack, 0}, - {LastStack, LastTime + USec} | Tail], + [{Stack1, 0}, + {LastStack, LastTime + USec} | Tail] + end, + %% TODO: more state tracking here. + PS#state{pid = Pid, lastTs = TS, count = Count + 1, acc = Acc2} + catch Class:Reason:StackTrace -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, Class, Reason, StackTrace]), + PS + end; +doExpInner({trace_ts, _Pid, return_to, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> + try + %% Calculate time elapsed, TS-LastTs. + %% 1. Credit elapsed time to the stack on the top of Acc. + %% 2. Push a 0 usec item with the "best" stack onto Acc. + %% "best" = MFA exists in the middle of the stack onto Acc, + %% or else MFA exists at the top of a stack elsewhere in Acc. + [{LastStack, LastTime} | Tail] = Acc, + MFA_bin = mfa_binify(MFA), + BestStack = lists:dropwhile(fun(SomeMFA) when SomeMFA /= MFA_bin -> true; + (_) -> false + end, find_matching_stack(MFA_bin, Acc)), + USec = TS - LastTS, + Acc2 = [{BestStack, 0}, + {LastStack, LastTime + USec} | Tail], % io:format(user, "return-to: ~p\n", [lists:sublist(Acc2, 4)]), - S#state{last_ts = TS, acc = Acc2} - catch XX:YY:ZZ -> - io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), - S - end; + S#state{lastTs = TS, acc = Acc2} + catch XX:YY:ZZ -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), + S + end; -exp1_inner({trace_ts, _Pid, gc_start, _Info, TS}, #state{last_ts = LastTS, acc = Acc} = S) -> - try - %% Push a 0 usec item onto Acc. - [{LastStack, LastTime} | Tail] = Acc, - NewStack = [<<"GARBAGE-COLLECTION">> | LastStack], - USec = TS - LastTS, - Acc2 = [{NewStack, 0}, - {LastStack, LastTime + USec} | Tail], +doExpInner({trace_ts, _Pid, gc_start, _Info, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> + try + %% Push a 0 usec item onto Acc. + [{LastStack, LastTime} | Tail] = Acc, + NewStack = [<<"GARBAGE-COLLECTION">> | LastStack], + USec = TS - LastTS, + Acc2 = [{NewStack, 0}, + {LastStack, LastTime + USec} | Tail], % io:format(user, "GC 1: ~p\n", [lists:sublist(Acc2, 4)]), - S#state{last_ts = TS, acc = Acc2} - catch _XX:_YY:_ZZ -> - %% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]), - S - end; -exp1_inner({trace_ts, _Pid, gc_end, _Info, TS}, #state{last_ts = LastTS, acc = Acc} = S) -> - try - %% Push the GC time onto Acc, then push 0 usec item from last exec - %% stack onto Acc. - [{GCStack, GCTime}, {LastExecStack, _} | Tail] = Acc, - USec = TS - LastTS, - Acc2 = [{LastExecStack, 0}, {GCStack, GCTime + USec} | Tail], + S#state{lastTs = TS, acc = Acc2} + catch _XX:_YY:_ZZ -> + %% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]), + S + end; +doExpInner({trace_ts, _Pid, gc_end, _Info, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> + try + %% Push the GC time onto Acc, then push 0 usec item from last exec + %% stack onto Acc. + [{GCStack, GCTime}, {LastExecStack, _} | Tail] = Acc, + USec = TS - LastTS, + Acc2 = [{LastExecStack, 0}, {GCStack, GCTime + USec} | Tail], % io:format(user, "GC 2: ~p\n", [lists:sublist(Acc2, 4)]), - S#state{last_ts = TS, acc = Acc2} - catch _XX:_YY:_ZZ -> - %% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]), - S - end; + S#state{lastTs = TS, acc = Acc2} + catch _XX:_YY:_ZZ -> + %% io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, _XX, _YY, _ZZ]), + S + end; -exp1_inner({trace_ts, _Pid, out, MFA, TS}, #state{last_ts = LastTS, acc = Acc} = S) -> - try - %% Push a 0 usec item onto Acc. - %% The MFA reported here probably doesn't appear in the stacktrace - %% given to us by the last 'call', so add it here. - [{LastStack, LastTime} | Tail] = Acc, - MFA_bin = mfa_binify(MFA), - NewStack = [<<"SLEEP">>, MFA_bin | LastStack], - USec = TS - LastTS, - Acc2 = [{NewStack, 0}, - {LastStack, LastTime + USec} | Tail], - S#state{last_ts = TS, acc = Acc2} - catch XX:YY:ZZ -> - io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), - S - end; -exp1_inner({trace_ts, _Pid, in, MFA, TS}, #state{last_ts = LastTS, acc = Acc} = S) -> - try - %% Push the Sleep time onto Acc, then push 0 usec item from last - %% exec stack onto Acc. - %% The MFA reported here probably doesn't appear in the stacktrace - %% given to us by the last 'call', so add it here. - MFA_bin = mfa_binify(MFA), - [{SleepStack, SleepTime}, {LastExecStack, _} | Tail] = Acc, - USec = TS - LastTS, - Acc2 = [{[MFA_bin | LastExecStack], 0}, {SleepStack, SleepTime + USec} | Tail], - S#state{last_ts = TS, acc = Acc2} - catch XX:YY:ZZ -> - io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), - S - end; +doExpInner({trace_ts, _Pid, out, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> + try + %% Push a 0 usec item onto Acc. + %% The MFA reported here probably doesn't appear in the stacktrace + %% given to us by the last 'call', so add it here. + [{LastStack, LastTime} | Tail] = Acc, + MFA_bin = mfa_binify(MFA), + NewStack = [<<"SLEEP">>, MFA_bin | LastStack], + USec = TS - LastTS, + Acc2 = [{NewStack, 0}, + {LastStack, LastTime + USec} | Tail], + S#state{lastTs = TS, acc = Acc2} + catch XX:YY:ZZ -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), + S + end; +doExpInner({trace_ts, _Pid, in, MFA, TS}, #state{lastTs = LastTS, acc = Acc} = S) -> + try + %% Push the Sleep time onto Acc, then push 0 usec item from last + %% exec stack onto Acc. + %% The MFA reported here probably doesn't appear in the stacktrace + %% given to us by the last 'call', so add it here. + MFA_bin = mfa_binify(MFA), + [{SleepStack, SleepTime}, {LastExecStack, _} | Tail] = Acc, + USec = TS - LastTS, + Acc2 = [{[MFA_bin | LastExecStack], 0}, {SleepStack, SleepTime + USec} | Tail], + S#state{lastTs = TS, acc = Acc2} + catch XX:YY:ZZ -> + io:format(user, "~p: ~p:~p @ ~p\n", [?LINE, XX, YY, ZZ]), + S + end; %exp1_inner(end_of_trace = _Else, #state{pid=Pid, output_path=OutputPath, acc=Acc} = S) -> % {ok, FH} = file:open(OutputPath, [write, raw, binary, delayed_write]), @@ -204,24 +180,24 @@ exp1_inner({trace_ts, _Pid, in, MFA, TS}, #state{last_ts = LastTS, acc = Acc} = % file:close(FH), % io:format("finished\n"), % S; -exp1_inner(_Else, S) -> +doExpInner(_Else, S) -> % io:format("?? ~P\n", [_Else, 10]), - S. + S. find_matching_stack(MFA_bin, [{H, _Time} | _] = Acc) -> - case lists:member(MFA_bin, H) of - true -> - H; - false -> - find_matching_stack2(MFA_bin, Acc) - end. + case lists:member(MFA_bin, H) of + true -> + H; + false -> + find_matching_stack2(MFA_bin, Acc) + end. find_matching_stack2(MFA_bin, [{[MFA_bin | _StackTail] = Stack, _Time} | _]) -> - Stack; + Stack; find_matching_stack2(MFA_bin, [_H | T]) -> - find_matching_stack2(MFA_bin, T); + find_matching_stack2(MFA_bin, T); find_matching_stack2(_MFA_bin, []) -> - [<<"FIND-MATCHING-FAILED">>]. + [<<"FIND-MATCHING-FAILED">>]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -230,45 +206,63 @@ intersperse(_, [X]) -> [X]; intersperse(Sep, [X | Xs]) -> [X, Sep | intersperse(Sep, Xs)]. stak_trim([<<"proc_lib:init_p_do_apply/3">>, <<"gen_fsm:decode_msg/9">>, <<"gen_fsm:handle_msg/7">>, <<"gen_fsm:loop/7">> | T]) -> - stak_trim([<<"GEN-FSM">> | T]); + stak_trim([<<"GEN-FSM">> | T]); stak_trim([<<"GEN-FSM">>, <<"gen_fsm:decode_msg/9">>, <<"gen_fsm:handle_msg/7">>, <<"gen_fsm:loop/7">> | T]) -> - stak_trim([<<"GEN-FSM">> | T]); + stak_trim([<<"GEN-FSM">> | T]); stak_trim(Else) -> - Else. + Else. stak_binify(Bin) when is_binary(Bin) -> - [list_to_binary(X) || X <- stak(Bin)]; + [list_to_binary(X) || X <- stak(Bin)]; stak_binify(X) -> - list_to_binary(io_lib:format("~w", [X])). + list_to_binary(io_lib:format("~w", [X])). mfa_binify({M, F, A}) -> - list_to_binary(io_lib:format("~w:~w/~w", [M, F, A])); + list_to_binary(io_lib:format("~w:~w/~w", [M, F, A])); mfa_binify(X) -> - list_to_binary(io_lib:format("~w", [X])). + list_to_binary(io_lib:format("~w", [X])). %% Borrowed from redbug.erl stak(Bin) -> - lists:foldl(fun munge/2, [], string:tokens(binary_to_list(Bin), "\n")). + lists:foldl(fun munge/2, [], string:tokens(binary_to_list(Bin), "\n")). munge(I, Out) -> - case I of %% lists:reverse(I) of - "..." ++ _ -> ["truncated!!!" | Out]; - _ -> - case string:str(I, "Return addr") of - 0 -> - case string:str(I, "cp = ") of - 0 -> Out; - _ -> [mfaf(I) | Out] - end; - _ -> - case string:str(I, "erminate process normal") of - 0 -> [mfaf(I) | Out]; - _ -> Out - end - end - end. + case I of %% lists:reverse(I) of + "..." ++ _ -> ["truncated!!!" | Out]; + _ -> + case string:str(I, "Return addr") of + 0 -> + case string:str(I, "cp = ") of + 0 -> Out; + _ -> [mfaf(I) | Out] + end; + _ -> + case string:str(I, "erminate process normal") of + 0 -> [mfaf(I) | Out]; + _ -> Out + end + end + end. mfaf(I) -> - [_, C | _] = string:tokens(I, "()+"), - string:strip(C). + [_, C | _] = string:tokens(I, "()+"), + string:strip(C). + +flush(#state{outputPath = OutputPath}) -> + PidStates = get(), + {ok, FH} = file:open(OutputPath, [write, raw, binary, delayed_write]), + io:format("\n\nWriting to ~s for ~w processes... ", [OutputPath, length(PidStates)]), + _ = [ + [begin + Pid_str0 = lists:flatten(io_lib:format("~w", [Pid])), + Size = length(Pid_str0), + Pid_str = [$(, lists:sublist(Pid_str0, 2, Size - 2), $)], + Time_str = integer_to_list(Time), + file:write(FH, [Pid_str, $;, intersperse($;, lists:reverse(Stack)), 32, Time_str, 10]) + end || {Stack, Time} <- Acc] + || {Pid, #state{acc = Acc} = _S} <- PidStates], + _ = file:close(FH), + io:format("finished!\n"), + ok. + diff --git a/src/messages/tpMsgRS.erl b/src/messages/tpMsgRS.erl index 78f59b4..77aa789 100644 --- a/src/messages/tpMsgRS.erl +++ b/src/messages/tpMsgRS.erl @@ -2,8 +2,10 @@ -include("eTpf.hrl"). --export([pfs/1]). %% 分析单个文件 --export([pfm/1]). %% 分析多个文件 +-export([ + pfs/1 %% 分析单个文件 +, pfm/1 %% 分析多个文件 +]). -record(state, { meta = #{} :: map(), @@ -16,7 +18,7 @@ -spec pfs(file:filename_all()) -> ok. pfs(InputFile) -> - {ok, FinalState} = tpFileReader:fold(fun handleEvent/2, #state{}, InputFile), + {ok, FinalState} = tpFReader:fold(fun handleEvent/2, #state{}, InputFile), flush(FinalState). -spec pfm(file:filename()) -> ok. @@ -27,7 +29,7 @@ pfm(InputFiles) -> doPfm([], State) -> flush(State); doPfm([InputFile | PfFiles], State) -> - {ok, NewState} = tpFileReader:fold(fun handleEvent/2, State, InputFile), + {ok, NewState} = tpFReader:fold(fun handleEvent/2, State, InputFile), doPfm(PfFiles, NewState). %% @todo Later we may want to look at the latency of gen_server call/reply. @@ -149,18 +151,18 @@ flushMostActivePairBidirectional(State = #state{pairs = Pairs}) -> ], ok. -flushDigraph(State = #state{pairs = Pairs}) -> +flushDigraph(#state{pairs = Pairs} = State) -> TemPairs = maps:fold(fun groupPairs/3, #{}, Pairs), List = maps:to_list(TemPairs), - ok = file:write_file(<<"digraph.gv">>, [ + HeaderBin = <<"digraph {\n" " concentrate=true;\n" " splines=ortho;\n" " edge [arrowhead=none, labelfontsize=12.0, minlen=3];\n" "\n">>, - [eFmt:format(" \"~w~s\" -> \"~w~s\" [taillabel=~b, headlabel=~b];~n", [F, label(F, State), T, label(T, State), FC, TC]) || {{F, T}, {FC, TC}} <- List], - <<"}\n">> - ]), + + writeEvents(List, State, HeaderBin), + io:format( "The file digraph.gv was created. Use GraphViz to make a PNG.~n" "$ dot -Tpng -O digraph.gv~n" @@ -169,12 +171,19 @@ flushDigraph(State = #state{pairs = Pairs}) -> "One line in the file is equal to a connection between two processes.~n"), ok. +writeEvents([], _State, BinAcc) -> + LastBinAcc = <>, + ok = file:write_file(<<"digraph.gv">>, LastBinAcc); +writeEvents([{{F, T}, {FC, TC}} | List], State, BinAcc) -> + EventBin = eFmt:formatBin(<<" \"~w~s\" -> \"~w~s\" [taillabel=~b, headlabel=~b];~n">>, [F, label(F, State), T, label(T, State), FC, TC]), + writeEvents(List, State, <>). + label(P, #state{meta = Meta}) -> case maps:get(P, Meta, #{}) of #{process_type := PT} -> - io_lib:format(" (~w)", [PT]); + eFmt:format(<<" (~w)">>, [PT]); _ -> - "" + <<"">> end. mergePairs({From, To}, Count, Acc) -> diff --git a/src/messages/tpMsgSD.erl b/src/messages/tpMsgSD.erl index 9842f11..08ddeaa 100644 --- a/src/messages/tpMsgSD.erl +++ b/src/messages/tpMsgSD.erl @@ -2,8 +2,10 @@ -include("eTpf.hrl"). --export([pfs/2]). --export([pfm/2]). +-export([ + pfs/2 %% 分析单个文件 + , pfm/2 %% 分析多个文件 +]). -record(state, { meta = #{} :: map(), @@ -13,7 +15,7 @@ -spec pfs(file:filename_all(), list()) -> ok. pfs(InputFile, Pids) -> - {ok, FinalState} = tpFileReader:fold(fun handleEvent/2, #state{pids = preparePids(Pids)}, InputFile), + {ok, FinalState} = tpFReader:fold(fun handleEvent/2, #state{pids = preparePids(Pids)}, InputFile), flush(FinalState). -spec pfm(file:filename(), list()) -> ok. @@ -24,7 +26,7 @@ pfm(InputFiles, Pids) -> doPfm([], State) -> flush(State); doPfm([InputFile | PfFiles], State) -> - {ok, NewState} = tpFileReader:fold(fun handleEvent/2, State, InputFile), + {ok, NewState} = tpFReader:fold(fun handleEvent/2, State, InputFile), doPfm(PfFiles, NewState). handleEvent({send, From, _, Info, ?eTpfHole}, State = #state{meta = Meta}) -> @@ -39,54 +41,56 @@ handleEvent({send, From, _, Info, ?eTpfHole}, State = #state{meta = Meta}) -> handleEvent({send, From, _, _, To} = Event, State) -> maybeKeepEvent(Event, From, To, State); handleEvent({send_to_non_existing_process, From, _, _, To} = Event, State) -> - maybeKeepEvent(Event, From, To, State); + maybeKeepEvent(Event, From, To, State); handleEvent({spawn, From, _, To, _} = Event, State) -> maybeKeepEvent(Event, From, To, State); handleEvent({exit, Pid0, _, _} = Event, State = #state{events = Events, pids = Pids}) -> - Pid = hide_pid_node(Pid0), + Pid = hidePidNode(Pid0), case lists:member(Pid, Pids) of true -> - State#state{events = [Event | Events]}; + State#state{events = [Event | Events]}; _ -> - State + State end; %% Ignore all other events. We only care about messages and spawns/exits. handleEvent(_, State) -> State. maybeKeepEvent(Event, From0, To0, State = #state{events = Events, pids = Pids}) -> - From = hide_pid_node(From0), - To = hide_pid_node(To0), + From = hidePidNode(From0), + To = hidePidNode(To0), case {lists:member(From, Pids), lists:member(To, Pids)} of {true, true} -> State#state{events = [Event | Events]}; _ -> State end. preparePids(Pids) -> - [hide_pid_node(Pid) || Pid <- Pids]. + Pids. +%% [hide_pid_node(Pid) || Pid <- Pids]. -hide_pid_node(Pid) when is_pid(Pid) -> hide_pid_node(pid_to_list(Pid)); -hide_pid_node([$<, _, $. | Tail]) -> "<***." ++ Tail; -hide_pid_node([$<, _, _, $. | Tail]) -> "<***." ++ Tail; -hide_pid_node([$<, _, _, _, $. | Tail]) -> "<***." ++ Tail; -hide_pid_node([$<, _, _, _, _, $. | Tail]) -> "<***." ++ Tail; -hide_pid_node([$<, _, _, _, _, _, $. | Tail]) -> "<***." ++ Tail; -hide_pid_node(Name) -> Name. +hidePidNode(Pid) when is_pid(Pid) -> + Pid; +%%hide_pid_node(pid_to_list(Pid)); +hidePidNode([$<, _, $. | Tail]) -> "<***." ++ Tail; +hidePidNode([$<, _, _, $. | Tail]) -> "<***." ++ Tail; +hidePidNode([$<, _, _, _, $. | Tail]) -> "<***." ++ Tail; +hidePidNode([$<, _, _, _, _, $. | Tail]) -> "<***." ++ Tail; +hidePidNode([$<, _, _, _, _, _, $. | Tail]) -> "<***." ++ Tail; +hidePidNode(Name) -> Name. -flush(State = #state{events = Events0}) -> +flush(#state{events = Events} = State) -> %% Sort by timestamp from oldest to newest. - Events = lists:keysort(3, Events0), + SortEvents = lists:keysort(3, Events), %% Initialize the formatting state. put(num_calls, 0), %% Output everything. - ok = file:write_file("seq.diag", [ - "seqdiag {\n" - " edge_length = 300;\n" - " activation = none;\n" - "\n", - [format_event(Event, State) || Event <- Events], - "}\n" - ]), + HeaderBin = <<"seqdiag {\n" + " edge_length = 300;\n" + " activation = none;\n" + "\n">>, + + writeEvents(SortEvents, State, HeaderBin), + io:format( "The file seq.diag was created. Use seqdiag to make a PNG.~n" "$ seqdiag -Tpng --no-transparency seq.diag~n" @@ -98,42 +102,43 @@ flush(State = #state{events = Events0}) -> "One line in the file is equal to a message sent by a process to another.~n"), ok. -format_event({spawn, From, _, To, MFA}, State) -> - io_lib:format(" \"~w~s\" ->> \"~w~s\" [label=\"spawn ~9999P\"];~n", [ - From, label(From, State), To, label(To, State), MFA, 8]); -format_event({exit, Pid, _, Reason}, State) -> +writeEvents([], _State, BinAcc) -> + LastBinAcc = <>, + ok = file:write_file(<<"seq.diag">>, LastBinAcc); +writeEvents([Event | SortEvents], State, BinAcc) -> + EventBin = formatEvent(Event, State), + writeEvents(SortEvents, State, <>). + +formatEvent({spawn, From, _, To, MFA}, State) -> + eFmt:formatBin(<<" \"~w~s\" ->> \"~w~s\" [label=\"spawn ~9999P\"];~n">>, [From, label(From, State), To, label(To, State), MFA, 8]); +formatEvent({exit, Pid, _, Reason}, State) -> PidLabel = label(Pid, State), - io_lib:format(" \"~w~s\" ->> \"~w~s\" [label=\"exit ~9999P\"];~n", [ - Pid, PidLabel, Pid, PidLabel, Reason, 8]); -format_event({Type, From, _, {'$gen_call', {From, Ref}, Msg}, To}, State) -> + eFmt:formatBin(<<" \"~w~s\" ->> \"~w~s\" [label=\"exit ~9999P\"];~n">>, [Pid, PidLabel, Pid, PidLabel, Reason, 8]); +formatEvent({Type, From, _, {'$gen_call', {From, Ref}, Msg}, To}, State) -> NumCalls = get(num_calls) + 1, put(num_calls, NumCalls), put(Ref, NumCalls), - io_lib:format(" \"~w~s\" ~s \"~w~s\" [label=\"gen:call #~w ~9999P\"];~n", [ - From, label(From, State), - case Type of send -> "->"; _ -> "-->" end, - To, label(To, State), NumCalls, Msg, 8]); -format_event(Event = {Type, From, _, {Ref, Msg}, To}, State) -> + eFmt:formatBin(<<" \"~w~s\" ~s \"~w~s\" [label=\"gen:call #~w ~9999P\"];~n">>, [From, label(From, State), case Type of send -> + "->"; _ -> "-->" end, To, label(To, State), NumCalls, Msg, 8]); +formatEvent(Event = {Type, From, _, {Ref, Msg}, To}, State) -> case get(Ref) of undefined -> - default_format_event(Event, State); + defFormatEvent(Event, State); NumCall -> - io_lib:format(" \"~w~s\" ~s \"~w~s\" [label=\"#~w ~9999P\"];~n", [ - From, label(From, State), - case Type of send -> "->"; _ -> "-->" end, - To, label(To, State), NumCall, Msg, 8]) + eFmt:formatBin(<<" \"~w~s\" ~s \"~w~s\" [label=\"#~w ~9999P\"];~n">>, [From, label(From, State), case Type of send -> + "->"; _ -> "-->" end, To, label(To, State), NumCall, Msg, 8]) end; -format_event(Event, State) -> - default_format_event(Event, State). +formatEvent(Event, State) -> + defFormatEvent(Event, State). -default_format_event({Type, From, _, Msg, To}, State) -> - io_lib:format(" \"~w~s\" ~s \"~w~s\" [label=\"~9999P\"];~n", [ - From, label(From, State), - case Type of send -> "->"; _ -> "-->" end, - To, label(To, State), Msg, 8]). +defFormatEvent({Type, From, _, Msg, To}, State) -> + eFmt:formatBin(<<" \"~w~s\" ~s \"~w~s\" [label=\"~9999P\"];~n">>, [From, label(From, State), case Type of send -> + "->"; _ -> "-->" end, To, label(To, State), Msg, 8]). label(P, #state{meta = Meta}) -> case maps:get(P, Meta, #{}) of - #{process_type := PT} -> io_lib:format(" (~w)", [PT]); - _ -> "" + #{process_type := PT} -> + eFmt:formatBin(" (~w)", [PT]); + _ -> + <<"">> end. diff --git a/src/tracer/tpTracerConsole.erl b/src/tracer/tpTracerConsole.erl index 120f5d8..1658431 100644 --- a/src/tracer/tpTracerConsole.erl +++ b/src/tracer/tpTracerConsole.erl @@ -1,8 +1,11 @@ -module(tpTracerConsole). -export([start_link/2]). --export([init/1]). --export([loop/1]). +-export([ + init/1 + , loop/1 +]). + -export([system_continue/3]). -export([system_terminate/4]).