From db0e306b1c78a85c13f7e56c73a414bda86f468c Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Tue, 2 May 2023 14:44:08 +0200 Subject: [PATCH 01/53] Added rms_flag to disable gating under define BS1770DEMO_UPDATE --- src/bs1770demo/bs1770demo.c | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/bs1770demo/bs1770demo.c b/src/bs1770demo/bs1770demo.c index 0d51d379..8d57589a 100644 --- a/src/bs1770demo/bs1770demo.c +++ b/src/bs1770demo/bs1770demo.c @@ -11,6 +11,8 @@ #include #include +#define BS1770DEMO_UPDATE + #define BLOCK_SIZE 19200 /* 400 ms in 48000 Hz sample rate */ #define STEP_SIZE 4800 /* 100 ms in 48000 Hz sample rate (75% overlap of 400 ms gating blocks) */ #define LKFS_OFFSET (-0.691) @@ -100,6 +102,9 @@ void usage() fprintf( stdout, "Options:\n" ); fprintf( stdout, "-nchan N Number of channels [1..24] (Default: 1)\n" ); fprintf( stdout, "-lev L Target level LKFS (Default: -26)\n" ); +#ifdef BS1770DEMO_UPDATE + fprintf( stdout, "-rms Disable gating (for background noise level measurement)\n" ); +#endif fprintf( stdout, "-conf xxxx Configuration string:\n") ; fprintf( stdout, " '1' ldspk pos within |elev| < 30 deg, 60 deg <= |azim| <= 120 deg\n" ); fprintf( stdout, " 'L' LFE channel (weight zero)\n" ); @@ -271,7 +276,12 @@ double gated_loudness( /* o: gated loudness */ const double *gating_block_energy, /* i: gating_block_energy */ const double fac, /* i: Scaling factor */ const long n_gating_blocks, /* i: Number of gating blocks */ +#ifdef BS1770DEMO_UPDATE + const double threshold, /* i: LKFS threshold */ + const short rms_flag /* i: Flag for RMS option (disabled gating) */ +#else const double threshold /* i: LKFS threshold */ +#endif ) { long i; @@ -280,7 +290,11 @@ double gated_loudness( /* o: gated loudness */ count = 0; for( i = 0; i < n_gating_blocks; i++ ) { +#ifdef BS1770DEMO_UPDATE + if( ( (LKFS_OFFSET + 10 * log10( gating_block_energy[i] * fac * fac )) > threshold ) || rms_flag ) +#else if( (LKFS_OFFSET + 10 * log10( gating_block_energy[i] * fac * fac )) > threshold ) +#endif { energy += gating_block_energy[i] * fac * fac; count++; @@ -293,19 +307,32 @@ double gated_loudness( /* o: gated loudness */ double gated_loudness_adaptive( /* o: gated loudness, using adaptive threshold */ const double *gating_block_energy, /* i: gating_block_energy */ const double fac, /* i: Scaling factor */ +#ifdef BS1770DEMO_UPDATE + const long n_gating_blocks, /* i: Number of gating blocks */ + const short rms_flag /* i: Flag for RMS option (disabled gating) */ +#else const long n_gating_blocks /* i: Number of gating blocks */ +#endif ) { double relative_threshold; double gated_loudness_final; /* Find scaling factor */ +#ifdef BS1770DEMO_UPDATE + relative_threshold = gated_loudness( gating_block_energy, fac, n_gating_blocks, ABSOLUTE_THRESHOLD, rms_flag ) + RELATIVE_THRESHOLD_OFFSET; +#else relative_threshold = gated_loudness( gating_block_energy, fac, n_gating_blocks, ABSOLUTE_THRESHOLD ) + RELATIVE_THRESHOLD_OFFSET; +#endif if( ABSOLUTE_THRESHOLD > relative_threshold ) { relative_threshold = ABSOLUTE_THRESHOLD; } +#ifdef BS1770DEMO_UPDATE + gated_loudness_final = gated_loudness( gating_block_energy, fac, n_gating_blocks, relative_threshold, rms_flag ); +#else gated_loudness_final = gated_loudness( gating_block_energy, fac, n_gating_blocks, relative_threshold ); +#endif return gated_loudness_final; } @@ -314,6 +341,9 @@ double find_scaling_factor( /* o: scaling factor */ const double *gating_block_energy, /* i: gating_block_energy */ const long n_gating_blocks, /* i: Number of gating blocks */ const double lev, /* i: Target level */ +#ifdef BS1770DEMO_UPDATE + const short rms_flag, /* i: Flag for RMS option (disabled gating) */ +#endif double *lev_input, /* o: Input level */ double *lev_obtained /* o: Obtained level */ ) @@ -329,7 +359,11 @@ double find_scaling_factor( /* o: scaling factor */ while( (fabs( 1.0 - fac / last_fac ) > RELATIVE_DIFF) && (itr < MAX_ITERATIONS) ) { /* Find scaling factor */ +#ifdef BS1770DEMO_UPDATE + gated_loudness_final = gated_loudness_adaptive( gating_block_energy, fac, n_gating_blocks, rms_flag ); +#else gated_loudness_final = gated_loudness_adaptive( gating_block_energy, fac, n_gating_blocks ); +#endif last_fac = fac; fac *= pow( 10.0, (lev - gated_loudness_final) / 20.0 ); if (itr == 0 ) @@ -397,12 +431,18 @@ int main(int argc, char **argv ) double fac; double G[MAX_CH_NUMBER]; short zero_input_flag; +#ifdef BS1770DEMO_UPDATE + short rms_flag; +#endif lev_target = -26; /* Default target level */ i = 1; conf = NULL; nchan = -1; zero_input_flag = 1; +#ifdef BS1770DEMO_UPDATE + rms_flag = 0; +#endif /* Command line parsing */ if( argc == 1 ) @@ -436,6 +476,13 @@ int main(int argc, char **argv ) } i += 2; } +#ifdef BS1770DEMO_UPDATE + else if( strcmp( argv[i], "-rms" ) == 0 ) + { + rms_flag = 1; + i += 1; + } +#endif else if( strcmp( argv[i], "-conf" ) == 0 ) { conf = argv[i + 1]; @@ -583,7 +630,11 @@ int main(int argc, char **argv ) /* Find scaling factor */ /* Since a rescaling affects the relative gating threshold the factor is found through an iterative function */ +#ifdef BS1770DEMO_UPDATE + fac = find_scaling_factor( gating_block_energy, n_gating_blocks, lev_target, rms_flag, &lev_input, &lev_obtained ); +#else fac = find_scaling_factor( gating_block_energy, n_gating_blocks, lev_target, &lev_input, &lev_obtained ); +#endif /* Apply scaling */ rewind( f_input ); @@ -613,7 +664,11 @@ int main(int argc, char **argv ) else { /* No output file is specified -- find the input level */ +#ifdef BS1770DEMO_UPDATE + lev_input = gated_loudness_adaptive( gating_block_energy, 1.0, n_gating_blocks, rms_flag ); +#else lev_input = gated_loudness_adaptive( gating_block_energy, 1.0, n_gating_blocks ); +#endif fprintf( stdout, "Input level: %.6f\n", lev_input ); fprintf( stdout, "\n--> Done processing %ld samples\n", length_total ); } From 3c295d4ec3c4b8e8780868e4dcfcff65e63dec55 Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Thu, 4 May 2023 17:12:17 +0200 Subject: [PATCH 02/53] Added output of scaling factor in dB --- src/bs1770demo/bs1770demo.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bs1770demo/bs1770demo.c b/src/bs1770demo/bs1770demo.c index 8d57589a..923c9dba 100644 --- a/src/bs1770demo/bs1770demo.c +++ b/src/bs1770demo/bs1770demo.c @@ -653,6 +653,9 @@ int main(int argc, char **argv ) fprintf( stdout, "Target level: %.6f\n", lev_target ); fprintf( stdout, "Obtained level: %.6f\n", lev_obtained ); fprintf( stdout, "Scaling factor: %.6f\n", fac ); +#ifdef BS1770DEMO_UPDATE + fprintf( stdout, "Scaling [dB]: %.6f\n", 20 * log10( fac ) ); +#endif fprintf( stdout, "\n--> Done processing %ld samples\n", length_total ); if( clip > 0 ) { From 6e94fc7c3fa6d5e9989d210ecfd76c582c1a85aa Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Thu, 4 May 2023 17:48:24 +0200 Subject: [PATCH 03/53] Add check for all blocks below ABSOLUTE_THRESHOLD --- src/bs1770demo/bs1770demo.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/bs1770demo/bs1770demo.c b/src/bs1770demo/bs1770demo.c index 923c9dba..511ae71b 100644 --- a/src/bs1770demo/bs1770demo.c +++ b/src/bs1770demo/bs1770demo.c @@ -21,6 +21,10 @@ #define MAX_ITERATIONS 10 #define RELATIVE_DIFF 0.0001 #define MAX_CH_NUMBER 24 +#ifdef BS1770DEMO_UPDATE +#define ZERO_BLOCKS (1000.0f) /* Constant to signal that zero blocks passed the gating threshold. + (Only results within [-Inf,LKFS_OFFSET] are valid) */ +#endif /* Channel weights for default channel ordering. Assumes channels are ordered as in 22.2 WAVE files: @@ -301,7 +305,18 @@ double gated_loudness( /* o: gated loudness */ } } +#ifdef BS1770DEMO_UPDATE + if ( count == 0 ) + { + return ZERO_BLOCKS; /* Send invalid value to indicate that zero blocks were above threshold */ + } + else + { + return LKFS_OFFSET + 10 * log10(energy / count); + } +#else return LKFS_OFFSET + 10 * log10( energy / count ); +#endif } double gated_loudness_adaptive( /* o: gated loudness, using adaptive threshold */ @@ -432,6 +447,7 @@ int main(int argc, char **argv ) double G[MAX_CH_NUMBER]; short zero_input_flag; #ifdef BS1770DEMO_UPDATE + short zero_blocks_flag; short rms_flag; #endif @@ -621,7 +637,16 @@ int main(int argc, char **argv ) } } +#ifdef BS1770DEMO_UPDATE + /* Check if all blocks are below ABSOLUTE_THRESHOLD */ + zero_blocks_flag = (ZERO_BLOCKS == gated_loudness(gating_block_energy, 1.0, n_gating_blocks, ABSOLUTE_THRESHOLD, rms_flag) ); +#endif + +#ifdef BS1770DEMO_UPDATE + if ( !zero_input_flag && !zero_blocks_flag ) +#else if( !zero_input_flag ) +#endif { if( f_output != NULL ) @@ -678,7 +703,18 @@ int main(int argc, char **argv ) } else { +#ifdef BS1770DEMO_UPDATE + if ( zero_input_flag ) + { + fprintf(stderr, "*** Warning: All non-LFE channels are zero\n"); + } + else + { + fprintf(stderr, "*** Warning: All non-LFE channels are below absolute gating threshold %.2f\n", ABSOLUTE_THRESHOLD); + } +#else fprintf( stderr, "*** Warning: All non-LFE channels are zero\n" ); +#endif if( f_output != NULL ) { fprintf( stderr, "*** Scaling of zero input not possible, exiting ..\n" ); From 5bb6dc4da1aa334a84b4dc76be6e0ef26c098850 Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Fri, 5 May 2023 07:53:49 +0200 Subject: [PATCH 04/53] Added test case for bs1770 demo rms option --- src/bs1770demo/CMakeLists.txt | 6 ++++++ .../supplementary_info/generate_test_signal.m | 12 ++++++++++-- src/bs1770demo/test_data/sine_ramp.26LKFS.pcm | Bin 0 -> 288000 bytes .../test_data/sine_ramp.26LKFSrms.pcm | Bin 0 -> 288000 bytes src/bs1770demo/test_data/sine_ramp.pcm | Bin 0 -> 288000 bytes 5 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/bs1770demo/test_data/sine_ramp.26LKFS.pcm create mode 100644 src/bs1770demo/test_data/sine_ramp.26LKFSrms.pcm create mode 100644 src/bs1770demo/test_data/sine_ramp.pcm diff --git a/src/bs1770demo/CMakeLists.txt b/src/bs1770demo/CMakeLists.txt index 9a9c8fc8..15a8b7f3 100644 --- a/src/bs1770demo/CMakeLists.txt +++ b/src/bs1770demo/CMakeLists.txt @@ -11,3 +11,9 @@ add_test(bs1770demo2-verify ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/signal-diff -equiv add_test(bs1770demo3 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/bs1770demo -lev -16 -conf 11L000 test_data/sine_noise_test.pcm test_data/sine_noise_test.16LKFS.11L000.test.pcm) add_test(bs1770demo3-verify ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/signal-diff -equiv 159 -q test_data/sine_noise_test.16LKFS.11L000.test.pcm test_data/sine_noise_test.16LKFS.11L000.pcm) + +add_test(bs1770demo4 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/bs1770demo -lev -26 test_data/sine_ramp.pcm test_data/sine_ramp.26LKFS.test.pcm) +add_test(bs1770demo4-verify ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/signal-diff -equiv 115 -q test_data/sine_ramp.26LKFS.test.pcm test_data/sine_ramp.26LKFS.pcm) + +add_test(bs1770demo5 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/bs1770demo -rms -lev -26 test_data/sine_ramp.pcm test_data/sine_ramp.26LKFSrms.test.pcm) +add_test(bs1770demo5-verify ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/signal-diff -equiv 115 -q test_data/sine_ramp.26LKFSrms.test.pcm test_data/sine_ramp.26LKFSrms.pcm) diff --git a/src/bs1770demo/supplementary_info/generate_test_signal.m b/src/bs1770demo/supplementary_info/generate_test_signal.m index 2e37b2ea..e1e4f0b7 100644 --- a/src/bs1770demo/supplementary_info/generate_test_signal.m +++ b/src/bs1770demo/supplementary_info/generate_test_signal.m @@ -16,10 +16,10 @@ rng(1); -A = dbov_fac(y,-26); +A = 0.070880495302845; N = randn(size(y)); -AN = dbov_fac(N,-26); +AN = 1.636408779315202e+03; Y(1,:) = A * 32767 * sin(2*pi*997*t); Y(2,:) = 0.58 * A * 32767 * sin(2*pi*1033*t); @@ -31,3 +31,11 @@ fid = fopen('sine_noise_test.pcm','w'); fwrite(fid,Y,'short'); fclose(fid); + +%% + +x = sin(2*pi*f*t) .* logspace(0,4,3*fs); + +fid = fopen('sine_ramp.pcm','w'); +fwrite(fid,x,'short'); +fclose(fid); diff --git a/src/bs1770demo/test_data/sine_ramp.26LKFS.pcm b/src/bs1770demo/test_data/sine_ramp.26LKFS.pcm new file mode 100644 index 0000000000000000000000000000000000000000..ac1fd77475e7e04c2aa1c3f3f5c57f9c907b96f5 GIT binary patch literal 288000 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

_G!VqMTN3cCubA^t;N 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 z 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≻D=kW3iG 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 literal 0 HcmV?d00001 diff --git a/src/bs1770demo/test_data/sine_ramp.26LKFSrms.pcm b/src/bs1770demo/test_data/sine_ramp.26LKFSrms.pcm new file mode 100644 index 0000000000000000000000000000000000000000..6aaf78faf3b8756067847c7415a79fe23353a417 GIT binary patch literal 288000 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


%lV^1nL!W)q52yiZCOen&YvBKDcKT=1IGGg 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μweO$;gvbf9kK7;%c37}a+Y6oX zsFLfW*J6=)3CwY=@}2QNLtYuM7O;hy5UIheVM$U;BOrp zYZa{;sqD6NW+H-HA%2gOZR8Y*kS^9QYqj0iX$|kf(CF&e?KthJ>>Z2=gag!JdOd1b zMr1gVug7=d>+=e~o6ErsXLm4X=#!YZ9Shw#h37eN?pUCdj_x35o&DST8~NuLSx@$3 z2kL>77-RqG6mg4!?HCZ-5Pz0P0X}!Ie=fC@o(hyG03R|~zX#uj&(2@us&dQ0r+lHm z;C7;&zLDOl9y)Oklf2U-W8K-{TH~ngF>;iwB%5(YO8{xLv$KH@qH%*+wb(!KSAO?a+4Srj_{XHeniQ$dla9wP7ZhiH?!{}Auw>XgVGH=_4&55gs=4LR*;s9(p)d@_$5AvCgddi$Au%sJqm zkKk?d@g_K-mw_TO!`)pNDDELU9Mz=`Ka3y6x4^jDB95S!`HSg6x1vh;eZFhBS*%$+ zbButk`~W7Rti1&F_XL?urjV^9OuAaGbp$79y1OiL3{^NA-i^Gq%i@m`f8Yl1iT+Jc+_nNGmPQ6D%a7w%@pJiB zyvYsZF2bQAFckd_Hv~=rqwY^!jV%S2f;$$R`9SHFty_5d-N`_*i6qDrt1yu62jDC( z+)X{>z0&~(dV%NPjtC02Tg}JqOL#a zyNCNj8-R=W2(IP=91*hJ!}@^p)0#9UgOP=bTeqy)I6*zoOKwCJPM7Ei=gSM^=)BP6 zGlD5w!lma&@_YDW{0ijJS6nS_CcB$CK(7UktA2{fF(LXBT$2taYdA9W9MXdP zNxG2j*x@}^FS`b&O&TJ{?TCGk7lwOqzwd+Jj|lJ}!lodD4(0dr=lE@i0fIWen|;8% zq;KF}ia~Hj{J<=1&sd3QAmVcsryL@fkOibEsf5p10wgits%dA04>((-7Br2dcsx-H zx6dZ|-%!c)C*~A8l%x1T`~m(df0!SEIyMQct^-xW!qoqIUuSQ6&qJuf9e`4DxCNXh z_HxT>EhSAzY5anTgI)GEg7>Ab*EH z&(GrX@$135W@U>qIcXnt2h*PQJl@2a*i>*Swb04;vUgar^)J>hLduf?FIWNCfFx|K{sIi{G>4HNDh-S)@y5ry~O#~O^W=8<_8;;(YAOD8`$e%%;Hn^7D z0_33W^jx@)viY9l4(31OR1CVlyUF>_{%Do84q|V!k#wX2vQV6?u{z-d)kH5j2UR$b zXpb3$ugKBWp%_+UQvyTO=XdiT_}|!rDSUt*#vR59dPtuDj%$ie<}~=$qA?pd{ep7` z5mv`KN!pOiL?H!{=RT6LRt2d4X;CFRL|4Y%#tV8zcu#`s$b<;U1uf|ySB>A!f8q)M z8d;(MzZ8s<3zz6O+z~j@SHb%mw-R*#A8i2z{jdXe8|yOZKr#}BWI~>}PX4meK)e0r zc;OD}726k&B;|qhvcnfX&i_Q5)#Cr-qHGQ}Ka+<31#e1UsFL^M zGh_9^CekkB}9v|1IwXPtn8&+=|vOQs1rNw6zxiBM!j#*GyvOCKHGs-1j(p06dWEB9EgP z<9%>KS!jBbs4et9W;NRj8DatdnNJ}Eg-@shBHs--We2mC9z)gi$MJsZ&fp4e;zp~1 zZWretdowW5a6J3p<`44^(wlrCbFDV0Vh!Cvk*(1mv2vJGdgjgQZ%2X8U?zb{^zhSh zI#UZ71rs}wkDtn&!U?)T?*t!L)b}25TBsVgFh{z?odiAaxW!rHNfz?md}sPeb6~}O zR&l5T+0aW4gTIeSw8Nb;x~~}30TIxXt;~hFar`?zose6gg(M)At=xMy!dUb(+@$+A zI+=5cf$&9q22S7P>;MW)XU#-(eKDV!x>*TWt^uk{*w&pC$Z<1bkK_5E2wVZzQ5rR% zKDhdK+)(}%pHV0x{2|;0TD%O#DHofQVd=;I#lBiz74OY$8Vf|<00mvPKZ8YCLh_JL z<{k61nU^dj1)-c=v#&ZI+*G*DeskPPGyu+hk(TY&8zqYsZi-9>yowJUIRw-VWBjrp5c zza~CG5O(u9_#xaHb}O?O(_(r3FW?O-l6VnY7;P0P>}GR{*7~Fa_uxNTPPOw( zzGFC{eEd{&99$7qo%;3&>l&#}KAT6(L*^^9I5|W*SOt)S@`G`m4@SBerjg%z3;PGc z7j%Hx!A<~1Il-qF8VN0g!omy0@KG>Y5$JALz{gef{lMF5%E!OMJ2=Yy+v#b~wVsj& z9m zPtmRBW-~GbIc^9#nG3j=D{bt4WSu+NnT!bgMp}_y=00_kPUu&UTWjr2&IQ+v)Qqi+>xss|xyk-~bbY2d`n(wTFE0x% zg+W3GMDcz;9p9cC#SUj$(^;vfxSK1F=LT+v%@_TJPWZI_(2}hFgqXX{`S=Ad&06Fc zSz-;dhl3Hl7750B#Lp!%dxv8}AVKAY=OR1zlbg@8LMvf}&_^gLT<6R26M%F#GPp$x zY|0VLgGX^MTFYqa$WQn}e_2_r(IkrXXP65S1qDbF8DurE>p9)r6_MA`;&8A1^3=u* z%Tvn7`~kPZQ*I*f!TMwI2{nWdsKcAl4c}%CgD^N!h5XoIk*~@%Wvel%=|{L-ERXjx=D38|X-tuJaXKKvjsZa&Hiw(t@r-Vn zHG$;zq1)c+JaAKDV*6Y?2mGyXe1)k#^n7MHJ021Ig0Cmc6*dYpgsQ?#z7XGo8^aD{ zYJ<@|=4b+s5{}$3NyQ^kPrx^;C1eh1>fICvL=s zpp(?lXS}v(t0lRO=Tpb5XO1zSo83uz%dm7i1$4=!(NL@fPG}b2C^)ksbUHQ-YUCEa z2%i5b;k2+oD1|KTK%dXavdn$xss(*lq02{rBAQ3iyD;8?3GGInV1H|vmCfF$1b-5f z+_J9P@0~QbSK~x13+`?D<*g64?-5NHhkd~<<#P+Og>%9+>~TZHaAv+Pa!^^uPoF>) zj^fR;h2wXSqpP`@oYZ!4YZ!Tl^I63#i6^wv%!h7l3r^5M=aX9qs@sov3(pB}1}HMn z-kDeIMQ$o8@O0sl@IW{ubQ8YvRr$f(|Ja_WlF4|VLviobMCW)Mw_Wr`4Je5Sn?`;j zzABpe&D!Q-Qv{M5XAOb_?2zk3nt}1oiaWf2_^M$BZ9j8{-HiwiB6_a~uY?Q2Xu&UZ z;a70m*=6u9WT$RmnlzeN94``k0!_7>(;PKyB{JM3v%HzjEQ8Mpne$0&s}{Wc6Wz;^ zOwgTwBwBe-_)-C-%wg8Ei@46bANl8+@JYCj9mpz7;ZJi9*h|bRupJ5D#A2Sy;B%?y z3HYXl*ke(H{bYt&&P-?KGkcm3%>kqs_>|mE6L(SMYqUNX^&IdmeD)WodomN)kz4~H z=n+`|m+%&6w1TjO{|Kb30cXvkO8W1?Uui>eC=rDR7mDg~>nuq@=41U7W(KplnPfI3 z0+cb!&g^!I90FT2F_G+P19vEcDf3QjN3IP2jUR~hHQ~2#9JSyQ&+~b>Y;1%+g|@qGY(mgks+4j`G}@$RyEg~g~(@e*}4v-ULZ0q`Zm@8Cp0(w%P*<) zOeLrd+4yICPdtB1aD&Qd{eG5TZjomSFMO%`|pGS6j3n}fnIXQ`bmnB_1Is= z^qPgt>84E1p_f^0pL8s@Zgdw^!)fppw1t0MrPG6n;Q4EON1UIiK#8w$l6*pQel$+d zU+5(tpbFF8WpKWnLyper+Nd#ps~Xvf^XW5*kpX!$WUeJct%3F&=Mr$-u-LnJQ%uR^ z^G^m&CkzcH>loi0=RY9?#AIQekWCoQZ{&8c^H3#OYP+wZ_eP?9T*bYW4G{r?ozZHJ z3|hlX7{3f2G4Q|~4J2B_?%*tP-$bgw8IcjQu$HeSwH_$w3;PTaTw6FP_{4N#LO22> zu@b!fBlve_(Uqvz@F#ja^W!;U7ojLscM70}^&sbvA7aKw!!@b_4YeUT(JNu@)5SXr zVngF!5^bRh=K)ID!(3s{a*Oy1$UlBDyQqjag?7RTUgbR8Z%hgdp;GyGK=Zi}Zy8f@ z*F`U&tR|?zw~^nX#!KU;QNUbfmL^g1!;07iG0*rQS{aObF7FbsarNox(0jH3r56(R zz3^})go)63?{`wp1*z9{8`|goyyJxiq0%-$M-KP77$IuSQNZ*YpwyCjd?*$ z@Tc}|!U;`^HI3Tv9nG_+TU(GJ{xajn9pj$iLnSlJ17s<1&{ao-cLeWj!3izwTj|%R zvP>7YA8Mp5tQWjuDY2}`i93NsyMgZ*1NTUPKH_iadj@~#uh^t$uE=ZW08rdX5<%p8 z%?SreS{kG5Ex>J={GMM$MJ+pV268>>Q;5TyR#WNt}8m3 z#^75^14n;G1Qg}6qXV8TM1)FWBe9V9LTE4S;U7W$KY}WmgF4}>?Y*6775@aTqZn$y zdoWE|$XJs#FBtobTZn-bW(^?GKj0D_G2TmgX2buxAHJHbj@M?a5@aT_!#QKCamw(cj%9_m^wsiV zZhjuTDZMZ&*%7L6c}z*(VM6S0?h-#l2n*H3&SFFCz*0fubMwWxwCr1YK9vtRv7+Z} zyk6{WWV-t&P*!%UHZo8utiRUSWBfKcnKAP!Ibq$mSU&IqdL}RU$)tOSDqt8zNwZwTV16%bHh>8OCTlA;TDB z79p~g%C6{4a^FUppp$6;zO^QBv=0%`9ubyJxG0nr|0m8B`ytOQ10%ul6!!*IvNClW z_u|}3G>tz2*OAJ7Y#+5wfhDME-ZZ8fgN%8`E2E<+fN}a{K`nE4MAF6YVlnYQffI`JCAlDbmmWqj{@Gs6GaWd0b)+i#fTz|I5;L2c z_l?O$Ph%{0po;m$JVB0HPwlks5cu!fL6z+6edTKeHc@5LahcEs>=FuzsC-bQ>$>?MZK$K-SFPe+UENdG$ z^$NKAC_5a$`Mf)PnJ{B@ogu6TJj!Yzw>UxECaxC$5`PO#fFtH}L!s+^fGQUCj`ry2 z#A3Kn3{&!Uz{*Dkm~Zj@+u##c8ML{<>_a-D7u$zUU;s3eehJkx)c3>RjNZUJU_WxV z__;!6eE+@TPVpa+7l#PD_!B_V{h9Riet&7-A)L@1(Dbgl!<}k&A*(7GXMQuL7|rlE z)%b2qK#nK|95lwc<(7`_i|-x;NBI8nwSCXZa1*n0HbKebfbY$%NT6jMGR*o6i!fWXBqgo;V~yZ z32fFhzfLt|X0cnjHGCg|7e|Z7#Pi}Rv5I&ItXxW-<{p8w&qnR=fyeEX8wy*HblTQZVKN6L^QAbBJ&hdq9+Os&pa@(C*FoU>nq$o&|Cziw-}uugYBV&~7+K7F z=00-RdTZwcqw9-Jj|btv&WsrxgRaDO=X&!s1yvXcU+o1(t|F_`i>QqL z6<*mh88~-3P|7oVFZ#S|W*T#eQPaq6R74be&=pT5)2uzV=HNEzXhTe*b@hJpb)l}( zf3P*UhUfx53Vp;g;#={yI71AGy@W;lVs0Q9(d$%8|9#y0aw1+bc0AJ5^*Z;g3*@z# z%ly}-uDBOSF&RjDDN~-l=CAELffKqu zmL<9uns7QhPDE16+>O%{Gy-@^yO5=M;FQ#MGiSG(F}fDIb`rRPYyOJ#a^?xEao_p> zgr?$2Q5P-os@Pq;D~Liy-p^fUhR|MWqR+s*{1Z5XUc(nu+0Ja`BCX6LMr}hh{Mh3K zh~W?BU7Vmy&QSLQs&FVi5p32{zX-)@Kl_lo%Wo9^6pxFx=$Bp~(!xRsp$=b~i^Jhp zkva)YW^F=_Pmd~*-H3oHRu$446`;PsqP@#x495&88 zyDBr2J%k9G4+L>ojEIW#TU;py#kQz2J-8C=J9s;YuaEaB{H-UUnmw5Ko%V@ ztml_;z1VcjKB}013v~1Gz`6ebr5v^=Tf@mB^QFaVc%xWB=3 z1jG$kKfjbkdMox9uL?SJlRNBmCOf^&pV2qNqsBYOo?*_Gb#7P($Qjcyy5svl(qHIl zkc;w~wrP^|c4zQqm2f9QhlK8#?o-f7eq?e%4=w`?w?d4I#iYWLDb5mq2$_YPyq7!0 zw5Q+u+xRYc3M7`tr07z&q!Y64qR$r00XRLk^;^2v=wndkUGob0V&%g`sTX&_XM--^ z7$~A8eT<2-*@1F@3-hqQrKJjzAZ-<8@lT-{a*)m}p)*lSFv-;;aR<(z%Wh||xgjup znaoiJV_ehE>A&>Gh~e$#Dx4tAZVHcX6U`aXu?ZwWZ9` zIpFC5!g#(Xm!G{ww}I+b!Mh>B#z#g8<_{{{K}#S-&8dc9T+$Eg&-Ien;R$9xGRZoH zDmgdu2kuX->3QWH=nqqWAp+iV-}tM-2+D6hm#}gpf)^rs&+9w&t9k}wgVENkN}5_T?6*#{$g^nwIE(inl=YvciZau| zcHiO;A;Tq$)ufJ64M|59Jt;ipuW?J+D$H%Fj{lH1jb{vSZbzV$1$Jwz25D!mGg2Gp z^bPt^?7$>o=~N`E)!g0&wqtd)So~I^5BS{Kl!xiat^`V4BeX|;t1k7BT1cX_0NspV zaJXyi2xtORpz1aE9ESUGVI-ZKWRJ1_CWFnrMkeE|zCz!MGujnM>zf%OnSrxjx}BqX zY+<5^_nNO2b&oF24nrR{O=uv#!}SO5$-d1oECv+bC%VXRW&J}ANYUEv`2%i6JeWrd|&ud&Z zmYNI65i4%@gswU|mIf!ZHBiI=`Z<#qD6WN&5B0IKG+LSxVlkYHc z^lm1>7URncsZrl6NaLkBctSpDkhn`Y%CCe!NQKM$BkmU4l<>xT14s7<*Yut|HceFE zYx*R8kiJY0>l0B0N|GklLR86tkwkP&yc*ntWBnYQYfo4i5#~h(EiFxwmP^AC1B+1S zKk+x=04vBGz%=6&_%|!Y*TdVl8j2oesfgg_h~M#gcYU({MDJ{5G@(yg?coA05jh|2 z9QS*+d;i1)LStqR`-AfcKZWhc4^yO#(hRArbWn7J6haKs8a#tu{kmVR#jAUW*`4soXfCtgYP_ez+N$qe7rW$pw2SXhbt zHdWdst(2Nb_rKEObUC&+QPYW}}Y*_!0v_tA8g~dO`K0;@{F!z+{ zMnCgc@-6oymT6i8;ymg!LQf&Gh+*v7N?bb0$;=$(i8dHg&K;hG&(p zBz1zu6g9V*-y)0@)8hFgNynsdk|Om*j#$Wd1yWxR@5mV6Bb?Bw;EOxE$@Xe%0ONl*=YCC-A2SPZV_lC^+LF}E2G&QDdnwB8L_sEbh;oMjX6O&o5h z%NM&7ALGg8yM!Elo5_b7GYA;Mi*J5Wx{oJR2Rr;t2;l@xV$(2-D9hK(yA|H7w$UqY z14O_fFj*%I+L)$S)bs02^xb+DQ~}on3v1s(j{6>67_WpIHE_p2y%sf~5+bY;GUzDj ziu77Ki5Pf@I-f;Ia7k=4<_=u&3%wr`W#WsX5f^uj*iXnQL~tr&j$U5RqF2%v0ZF_x zZkk_7LAYO1MRrGEZsnn3enos&Mhh9;b#KAur4?Mh;_p+aEabTw;(n zo&PXZ5sI6}r9lmTD)y7EOJV7)v`k7Z{Vk3bdhq$UtC)!W&#(A8d-lia7~Z(z3_>UG z0Hv2OR_SGQNl&kL)?exKj3MS^f_W2Xm0LLaAT}vc0*b>pif8`E-r_8t2v@{zSl^aH z(q5^QG+o>#tl&FwUUoXI`5XDxd49*M#nwUbS!36=GLST8O=AOblKt`yDXr8@Y$ufFzk!YTO*Qhbg*&czdIWVCOQt>!M{z1~OX)&)&ELHtw8L&(_H$@F<$z3hQ6g?rUjLi5nOt&H7{xbiEN^jL*kLCrWw0 z`xaAqm<_DM6%>k#jI<5wSCh-irZfgv_mglBF37^{PFkiq`?h<2VGeCh#DKFeqxHpn z1&r8PSG7~xc`c!h&~q7#nHEU;s9g^JxJj|{xOHc*uN|e)|A5O9gtX{k)<}w62Y-^h zN(zZ7M4xbt>&WKI1=}plaakTfpCUEI!;*0*b~AIt4ox zHRi2wQ=BGoa&x&W_TUkAxVP9tD8s*Dy8*}L@b~rZPW*;1VyVkHtE{Gou&TynJ)-T= zR%&OoEcy|B3aZS0LSvpT6>g;%5ijjAyu18O>9?o>>-h7+QE{Z?!?zzOH+Q?*850pKxFg10P)hC}=ooY_>KQ>Rx@9Hc#87`SitlJEOTdhTOEOLH8UR zEfqHsM}TwF(R%`ttRB9uSk~_^!h3MSTN3yW+p2l_ML$hF&5I%o)Z(J&5(kXfw1s zS`(cGjtG*rP=;H8F+KpkvJ5)0ZB#AhDw~h*BJ>yQNI$XuY$~_ht2DfebcRO?#(u{Sdq(++Q_tw0=(IBmWq?thNG;?Q z@&w=vD{t3O5%yuDpMw zcVFTwd=dY1Kih+>%!n|LQAPhx>!vl*hH8(rL3%agPjeJ`Y_)>7V_UR$yf_qrB!3^; ziyCl`cLhT{Db<(P%SreR739s*WAPD`y?WeDrU*UOf5ID1ySr!_pd! z^d#+Xt%lZFJFYd*QyZzw>SUFb#yRMAi{^%Io&=m*3@D`$x08P%ycTy$HRX-+C3zR( z;ELptXz>LcT0A?LKI4yhOL%(4H%EWNU3|mpN6MJRjKB2rS|_a>e!*HTpZ-(-Xo#c} z7}3FQw&;i0p2TQx6?pGfF%n?S#)BjsBTO$ z+lF5u>=b87CGhGf+BW?j4`_#lk|sp zHhHyzT0iZJ_Aij)1|aDU_HXCkNc&hGoY3>WiBuuxG+T%tCd?N{NO^I3UgK}0TtFTp zEfL2ECHYHmG0&i`LDw#n=nNDQb~@VM$vShQu~dJfb=9(I8MPML6>X^A+UO5mDYL!D zX%k5k`w;)nGs#yAY~LJo+D(KZVizf+yjK1o|H2;Emp4l1#eG75-p37NZcviHns*@B ztQW}9^X=5uMf9mV^`BY~tS)PnwC!3$J-<;7e9b$nzw-xFw{`Kso@(%}oub>akGKLt z3$Y<8z)Jb6?8tZJ-ttu`S$r>Sg}dVja$HmYRPVvWyI8tt7x#i))B1r3I|l^OS4*pT z@r-6`dG)BypxfST6?E>q^P}zIFiv={`zO=6*lnC&D1{pDk(bCnWLo(sPm_O18KghN z*Zf${1f#puf6Z&+-mKQq)vjd^u~Lv%##3atAzEt9R8wgKkVWt54-J;|w0_zP-1^X+ zzbDQB=hg*E8Ogom{}3|)Arno=myIyO7<&?#e| z0E(+=G}BjLe;=xMR7U%sRzOc_6oIbv+nVZ>i+C}~b;vW@*9L6gLAEHEh<)M~sTyD9aGTwDcP%s06o``GG3j&^tJ>B6bZqx{v)0Y<(A_m%at(^NxB}4XGV8Ke{o; zr~-db4aaa#Mi<2gdg}VpL6I4Y2E48r$ z?d7@BOtG2po9ha;LkCW*>iIi9BYN5uoe{9~Tr>{o540lM6?KhzP^Gk`S|{)YOTZ|O zxAVF`B4=ak5)-`b{JH2C%n8Tt8&xj8IhaIe-s3CNXPN~M=4{J%E|>G zp^TCxZ02)uQ?RoBBa7N_9EbMpxF}fd{;Q9-D5W({iztfd1$|NP5G6~(%3bCc| ziEG3zr?2|IdJ~utC>!nV9<>FEXNLPmmh=kn-5NUA^VC}sG`2wo{sU+(XH-x zC~gnUnW({Yw2$g2wV~QYJ)+jpQtKIwR^}Pf#P+#&B70-Y6O+9?{guF_tl`oMJ;kX~ ze>pwYPf~U&?Ub9cSB`^K?*g2aooPjl^v&~ZgyQhg&Ebr<66PLbw!TjLh4pKyjn!3Z zPVKw)UC(4rBr;T`8Ic~bCW%VkocPOP%#S_I!mVmLt;9Ib;%oUc}U zVjGwBtD0XMtCms=t3B1X>N0J*z85)YmQ}*B-N(_h@%^4P;367;tC`Q6LLsTJOytSR z9p$C64OwWSyab$OdSNAc$wH{YJ-ovcGh&;OqchvHQDZ&#lY z#x=8!_1iw^&P6BF3*M|sz|mI`0k;HEN+W-h$0-k$@5*VVxv~RQV5`_#c+QnUj$7&9 z=RK9U1qXc4ZHXFC7ZFB~L8q$))f8$u#K50g9w5=VCJQ#Ak6SZZIG)Xu!sq($(+kpMjN~H>|bQ4na>F7#k6^9 zew9{psFP5~Y)vqlLDlGP=YYrfQ|xKtruUeC3EhT`aHE8);z#MKJV<$_cmv5)N5$V)qJWOR@CmQt=`a{=xNO9B&+?> zITTqPTacLQ9R@ZoGkcY5AZ!+|N{8j1SYHf8l-)`OrJ-C)V#R-<>(yn3QnSHVu7^MO z7X0rmtefCM>*%eu^;lmGN5gg1N9sCllm5!6Lta@+oj&jn)=pIR7WMx@e_)nyS%h)o zE@^|@L3ygA3dn&gN)=_4JW8rAKIdz53z$>XBi~z`P#wODO3qAhS<8(c$Pl~ne7=W& zgbS+&)PIm8HX9sSX*F`PMOfVHV|sr09#Y$wzqk{wuFLF>Ti-r5> zbo5YsuV<@o5!Hvu!=2@ei2bEuat-Cak}Xgq;2_i82IFxEC+HS9`^I!{|4{F!#CRwg zM;rph@T;*!U#VSIi>t50H^Y(e0PL`&moVm-DXnw%e0L~1neLuWzQ(}O$%p`2Y#?=% zODeaN+<{7gU|_RCz)1WMmk0sA8@qx&=uhtGsR zg&V0veW^wC#^ybOnOir1Gz;8uX?-H~n?3>*^g_swE+(gPMadVa7swm9pkz@h$=RiQ z(8YGb9TfF*KEWf$(?(0Wz0n6$GQaCLwJ&NF^=bHE_-?oeu+SdulAZ!C&QkF4zl}VM z-A~-}Uh(gx|A$U^pFm6byM2_BxD_SmPTgArYB~2C;9u+)mVd@1SUn2 z*nMN?wN{uV4ZkjF&9MGje1Z|4 zqBhmq>;D=O*=hB2>PITaDkZ9TEBOo3e)bI4Tu2g=r62N6C4Zn-plcv~V1aT)J||5P z{}4uVNz5ziJEk1I;JyXoW^_7PSIqWC4n4cpQGFI(7G55H9`3B>LXPMR9Ms*)iK|J~ zsEQM6`o2+Dm>FCSVX=5udL*w^@&*P5h6YLn{!=vBkj|irodM&Thc4|e?Jbrl8Y>@Z z=Pa{`ImM{1m(}{HFT)GM)52%Nl~hO7wcN--*{qlL5%j`afM2-_l0R5Axf2Nnf-1PHKPTe+C@ zP^ir>Vo%byFlluelZF=}PaV$gK%RlC@1xCBzu?>V#&2-LTh#^HVO=p-k_L7*SHT=h zYENc3cR1!HJBLpx_K{}GgO#*_v4M5?PZJoV%#wRcDa2X)bLgJwsPw*|2iK}Z)44V6 z1%xtp>Ql8<>hJKBaL4dyMDaMavo==0XS5=e{n)t>IUl=`c;tQJze;aFC;VBcB@L82 zDN0~`U>ANt>A(slNnS5C7H{#{xTZ{Zs)w(mr&+vFw7ok4Tu=$~j=n+LqmuCCaIlw$Rah9>tnEbeBf|kYoJBowDJu& zVvZOPdU5|k6+7hH;aMGD8eQX_f*#PtH1#vuIn}F94%fvetPdAcy>}bxD>b^m>pmPHI%BdUs^2aToJY|-Oyj%TRf3FmMc=;>4#3cjuF>>s~Oap;qu|4 z;XdJS;T`IB?Hx|g53jPC@Sl|*f4Msc}>Euto|%3Ebb;6~tiU~`~AV4yNo zu7VR(o*&6BrC0eEc*i9M#`;7?J6kQ&9AaeC87;p$FI*~|HQY3O89O`--KuPEL6xlS zR)fd;FV6tqFsdh084)lV5%ycYs|*g@349Bj3N#3;RJNhYq!P#T=h*k)`(D9ed^~ml zs`)$AfVD1y5-H;sa8TP5~)QHx^cw)}Bx`Ivl zJ2pHq&%4&Yj-J6b;gf|<(n@)S(gy2`!B}8tAVr{@l1;vcZhkd)iMdBzg?nukI?;+YD3liSU((cLUqG0!w1z%S_<@H)xnqXxDz37qPn-Gzdc=-P0ODV%1EPu z;Tqui{1FTWuLQ~ldMRyWSy~``gf5wdPUH7@zr^oFufv(eSRKr_=(A_5Cvkedg}#Og zg^waj&(|L5O-+~Fv`@Mhqt9_dMW6_dea+1kB%s`WN|nIfK*nH>;DaYQN1268Gavn5=s$X9B!qy(^ldH9U>F$f$p&A z?D%%iIo}=XDzk}eE4&xW$jy~vft!Ks!4kn}U}E6Ba$cSezGeu&jZK2;w$?i_(IbW% zU!1Yl1+%pAQagzXP%iu`bTwp$28HvhxwXIan?^sVKH1#d(TeeQp5eY3)HJ3ims2=` z2rH*#4O|Z7304oL4sHtk0+KtAZu>ZNLzeMVzrA-72V-j^Yn|hkWp*_@`V%0aD&c3L zq|m!iqi`ausX6r7MsE1XA2?4UzhZ(Xzpn~aoypH7^D|JxG9$yC3FHqp4i*YtLKe-Z zM5N_ng0IN6WSUcze3?D5*q6wApp+tHk&#>1)M&U)_(^C_=sI@barmtIO{;IbG1ptu z&}poRol3m+n*JF5lRe6}0ZL5B5#>mrV6c6#M(|^xTA-a$T7Dwd5~g#Tn2pq2Uw=>i zc+qHXx3t}poHrWkK`o#*4L=TT4E={Qx+gqe-4CR@3OK8*Q#R5t)<3bpyT^Z=-ouXP z^P&%XFMm;XVg2sGHbGyocVM=0>BLaYDQJ|3N2N3w_22>AZYTS&!#4AlNsUH8?+T zLfI;}l|>S47C1IUM!^)yYjQx1@t(7M{lV_ zFa|OgY-L;Xrv8uCQXPTwGcz{SKm4!4Zaq^VsGRd{TCi|2;#BXaa@Zk~`; z8Y{0?CI@l^{}-GaY#n@t9ZsR#m0F9t`6s|}cl>+2Qxkv1sz)k1%@6^WzFZrqPRIG5 z66zmX8%h&?6n>2Nx!{p%3;EZ7A;DEpwrH=ektSAiP#zD7j;>+c+ zV~-=3fKsARgx_o1)m_Lx<3i0s1F-|+fu&b#l(Et5XLWFT11BC%y!SGgkx2>P)M8OY z4Q`11HZ^!8xHecNxFWDef$|_t7EW@vne$NfhI{J7vqc40u}hQbhM-?o&mw=04b={{ z#TjiCuBdj=F6!-pvm_@iCfd6t7JE>;R&IVq2i&DAqGe&tk%@;7_-Pgdzia8dM>VcvI9jlWXtl7Fjx8} zbAc~`3Bh~8XTcRgIano7NO^@Ew1!V&57R548kR{gv5(LszmXi~9KC>s9yL5AR5+A1 z)B#y&clfwU>9fE$WwUdl3J-{H^gQ(06bH@rqR?5oBqu9R0%L+tg5U9kN(B1{x+xju zf5k66%hAkt|0(Z`M9Wy=NJb|QA|MG&RyMUncvdKHC=jY1Iu#lho~&NcY8j^a06Fe= zBqQdCruz0#7nu{N0mTqu50uM+VZoO{J9rB*uqv<$Nc4?ZOlZZmX3+Edo+mcPMn(R1 zdRPn1Z~8DqaINsdP_B?SlrOX@)C}FyEX`xU2W(ApR!6SI;)&9}uGCm&4A)ZdNTcK< z%720W!8buB#i!t?;N`$=u(~;<(ZXhKJF|f5>MQ8cp$czzwprKB^u`*kz1k+cDwHE+ zBnPnrxx?wzM%rn;t69b>=F|mF+>`k1O+%Gt%5xcjvYMg>uMcz$z73{K5efbq38l3X_QAoO4OsajrtV5}hv>`m_ND3hq^{hxm`eVEc?H)+dLtE(i*Nv9fu~s7Fdn!O3siq8ipzdwcBh6Xn#Sq_ z7IY#|+0P0bpUpq!4sy@9`h0|s3OW93fpMYEk>0U6iOZ>Iras`gyHtlY%XN(mcZ>>1 z1JI#9G8}1+e1eO?B_X$QwBZ`?v)+Lndp;0-ks{LrTjix~Ya}Is%rp)#x?_pqp?5WD@ch5{28teV`Ji zGX}Z7oVK)Du6zMDfSaVH$@j6lk+&gJaDl&~XcgLm_$#RSBI{ol91jm&qBR{bOGbM-0Zba{1AFnwlsxej@N_Cz2UOTRVKM)X8^d+s7IIR+<-Q2z!QAJc(CEm5*u_LN)mpMy z_DLaEqo4l zn4zV9u(rFpv@&0|UeYivCh}t2fgAcZP}l!MxX*JSemSllx1DRo*9JVo<=+wnoBYvp z@j$X=W{1=(FR5y&X|5v-=ZzRt12%y8Kfz!42l)WIp$n!)#tr)O+GFZDU}oZ#Y)Cgt z%HzKxKSJu@aDPVl#wWNDToJAm$RZ0b1$urzVA5s7RinA_-AN8)=oa~BWk#*g{n9Ts z@+Lc+hIb$y@NYYkf^9Gdc*=LZRF_b{Q?8dcl6rwx&?R0v>I^puZt|BD1H6_W#hE#n z%jG!k6(1D(fiCtr^gSYvw@7YHbCTxr70NvI1MN|LPvdiw1m@u_2#3UwD@YlnJzO4o z1Td(JuCu1R$|v6{ZJ6;U_r}LX`-f))ANbpfCc(y!=O8Z4*5@8`yZP&a4P?;r(DKNs z7?Ws}-YyYjIm+?sh1#+DQpUq37xW8WhXfHl`X1>BZg?o9F-D z759r(aL2Yer9TAT&drsT?pKi zDEK~jPC6e!_ox||L*ch4w_XX$Q z4sDMnkpTduTSD)QB@K;ql{GQte_*cdO)pQ@iDx2oNEYnq{~+w)PjedX0(*)Lv1_4pwK?dWn)!~a5N(|E&r z-6Bm_l|peu+Bowg3Eub=tsHI;oac`VSNU68IdH?bpubGl41aWA0LBeaWWcwDYoxx! zH%BLhX9mxK3i|{Y1kN30m$Nt6hMbWvBP<4LI~q)dD}e2(52*%Vzcr(1sUE8x4V;3f zCI?&=@ge=t1!#Bl6JkIj&>oZASWRD9D^p)pGRHel#z9Ip_jCECdkF$sJ<< zVfV8ZfT9fFO#I?M9y}g?7X@!FO%-N3%1$d{sxn}vEj8{nB_I^}jSNKBqT|p6QXi=X z{|0lJ1^R{Bzkt%XBx@)64gB0Hu~ax1vhJ;Pn%rNDFT6YK$I z0{7xYQ!6qdX|Cd=>bHi`y)aBNeT9ihqq<4LuAf{fmSad?#?f9od%bI<`EA z@Xdv5;`l&rXiDTl%m}{8MN9sYUsp;sWq_MoU>pUVflq+^$wME3J7y3q@&g(LGU$!& znr61DxZ<+3S>|nW0>J2GND{2$KPe362ZH=;#nxaau@deVrxK=$#RF=vBibi+K9QB) zAd$;Qf{9GNwm^T@*c4h1Zv**%3;m4lMRBwNVujC|l*W>JSo={mSAocOXUeDU$0tRb zg&PLPfC`(>Pv*|EP1xdW5B3Xtl6%fq6+ikf1s{de(VoEZ&X$~%;mVQfUE0I?*+vVf z;KiVR@1b7wI$9qchxC90rmn`R`Vrcy>aU7%vPkCNlsR!Xx*|L$xF7Veef%=+I@^#% z*?Q~+b{4meXM~mhp~3Ot!_ioLSn7MGyX>yQ23X2)eM=*4>H<$dnxYTU5c&b!z+Pl6 zoDJ_+#3)-%Ba~& zS{sQ$GfY9upFy(xhaB{JpZACRf)VI1x7>@P$v1KSi-B7>Ra6#~^j#+aQBby`VX&YOHuH zZJBwJ>%b?f(wzeXnCv) zRuVgezC|v;?V$a}>-q!Q0qTTep)8%5m69g*M*j*o4fge)6I%1-xL)i><~Z}3X~&wm zYWyakm7Kwv;i=J2@y@CDnOxZiMUMJk?Oy!~V-9p2{vUD#t%ME48i5;-qa3^rVt^+d z(_UBS0-o4U@-fvX!AB2=7Xg;_3-sW|++g+tvy-{Zlw#d%i0dqH;`6}wkTp6pE+nUA zQqs8!pDJ714=8y(VCk2U9U%T#tT)D?HPLd&JK(AhG<4OOHF?S=^6Qf7>9auT3E{`V z|M`vLTD~_o4#Z!}>|rGA74`;a5Dtkm1B-wXu*WwfEtvz-Qi`oAN@LZVjo(cJ;oZn; zv?4YGn}DI%V017-z>7@h4JUMyG=yrO+$vp@)+9H_8bkmw3N#Wg1B95veqt6eOPQa{ za&{5Gh(2POK&8;&$jw-dAb>RibTyXyXVoR`^*jjW4(hGiR zk{L6)|A7t5G}zFk(u0#^Y-OZNsAXW1_=8`|t!BS5vzbZEO{O2)fm_K#0E0dTRgwO& zcZuAzS278B5uJfov)eGu1i?d*v1lo51-1k0j$KDzBU@ntWKb`lCEtNA{8`#2b2Hfp zVD$FTV$fq$!YOVq`MDsxp_yL@zmjFS%%mk(b zGasNt0c+sr3)%ki!7kwgQ8ck5RY7t?)k)3inf=(|*;zF|LH9NM%%yZN*+-|6#ST(P$Thf*PB88S3a5^*q26hfCh1 zS|(mar-$1BmbF$u_}^@T8O>B-+A`OedF&=G!0!|n1a^nqk#6xX$uSwJY%Or(hHAIy zw;2C|e!v8pLAPRGu!mSKwhLW_RE1ZY{xfXQwblGm=E@&Qs-*V5gd{sjkb*NLQz0i5W38@*a3uqA&m;goPc;SQs0~p`vUNfDtnB ztDg*3h-{1z$^QTk{VE@zdZN+loyL%9E&LnFM>k?D=EwG9Q8bNQhD$+RjLr25?Ov5r zu}n&*dnRAS21gtrMWD2}jIYL(WhXKwM!|FeIPGC8@Oc1(HU$3{t`4+to3vXpSFTn~ z*WAc<9(v>(9^&R(I9N(hOx_-4E>4@(NmaK>=^C`zgb)r$O|E0H>E`CerA{~ zsh9`cM8aS({eb!*>(MEg46ldV@B`R;^b*n<-fTKxn4v4GIjn@`6C^KEH4@;xSYcyO zP5adX*COq#w+f1^7vI9r9=%C{721^R`~M=HkeBnM@bvIB~8>J8dA`X9!VP&P6H z{Tsw@jn~AVU`k9t*1!d(2$<{5*QiueyX zm_o+ScHtw!Z@&a6foHM4$#nXNw58&ms+o3$ey?#R1OblN2b1F6@b)-^)y68J@8N1t z2P4>l)QnVplGg$mN`aZ4H@qwOum7N+; z>;f6;mCsU%8t_Xy#uktVZj83U(pW!y2oB-Hu;Hi$nG3qua(zSXb5)LFo0J9nvln7@ zBd*{*|7W2p|AxKCD4G5AX8I4k1k~_Tu0Oz__Q6eI1<=BE(gl(g^5Uv}ny{{@(FEOq zi=o+A6dQ(5$E)ERvAt+6@(g590r2cAtM@7r($1NqNke>9q-sbOFo}J60dyEWvx8m$ z?w}92;iViR6o_J=R`_7F8ra=gAgKWMBETC`bUzHAOzU6?S^^WX(fA6y3w{gxi0(pg zxV5Q)A)#HTmMI6y&S#XV5%J%V*`Zc}*5Yy=P?~Ib0$2Sri2WEzTMMeTY@mglI3fZd>@(w+)YHD3Eb?>0V%Mk9C7``85h0e%bbhabW=1E#nV+HIVyH)}Vk z0`g|kRq5|wH||DwNU*-Yt8kL*!nR^I(sk)N^hUZR(}_I}cuGD0py0J|{n)p}s&sSU z6>L*E!B)g!<7TJ{asWM#jm7_mf4~>u@3GhDEaWTX2U>5YHmb^19Fy|toaEM+DRMeE z*S}o&#EobBFx%*=ba8qDZ3Z)e20$W3- z=<#@N$_!>!ixgO18@wWRDf2)x5T?VVtQOEFev=h9G`L!b<#y z=VSNLuE-Xk=5zIoCRcSrPD#q7M<*UcbHYCY7saQ1Dee{X9K?T3eW#k!D$s{lam|1h zn-MGsPmPsK7N(C&hbv(9Zf#m$$&>{@K}um&@IQDRqB`*g$8ZS-yi15PUe$Ne-cccn zt{_8SB#Ok=hRX#*Vg~4Jn)yzbpdV5Xs3Pc4B)6Jv`#U)j1 z?FBt$^gz2273Rc$;7y1&L=bO*H^BZN*>Ee9#c)YmM*WZCnp8+vPR@!IhNl9hT}PP3 z=~$L7OW&YQQwi!YsNocQkY6WW4>-cdqTLf%`iJD8yf?t;;rgS-)6fLOjcV~vcpG8> zVJ4>FGqBR=5_pekn!%!5t<6YkO`>%CWNNyku{@^SsWIyZ8z+Mbdw>S9S9m945>bcPkDmif?mbZ3@4(zJ zpl+vJA-kOkB(viiBWUQp|CI2B>&#l1Z2ByqiZqKSQTF&Wc)ot-y#d+%~B@G zH`z{QGtDEN-Ixt}*go_p_6YArEFyXlF9C|4LW?17p{m9ox?If#Fn2B^>66+TrvXdR z1xP{1Pi3nzP3cqAG-?-Blupq#+3VaI;gCNNoEovkzb21nrU5_Kr=FsFYlxVBz%$W@ z*fqQdv5A;Mgz+LchmJ+|K&y<6^mjB4RX_P!$*oi*-YS|G>Jca}HsW`)?U;`ANooW& zALQs0+Rsb?Ot&@QS;olu*o0)G3@rPiSgkIqTV}{J9f!N1$FY+j{sCewQIzP8SHZ3z zBIGsh)0fdMRy~qOB&E{1iGxvncwb<&IE8=2j$rzM=g*~vQ?IDa^aZ9g=jIu)8t}{7 z#8kc0XH>C46C%Ps;XiT$k3aK zOmuLV37is7f*!e;83)e46V-t_P7S7~GC$Z&{6_J0pc$B}?n;bFHONUnOVcQvgQq(KE&j^a5#*O~>03w~3F$8p4Zv zuyv>h4w~*6dg*S17t7U__023!-U1VuRiFZj3LUs7pab8a8dJroA(WI((w$g8_eX#N zGXTatj&Ds(l(dzDU*^+v)E_ZEfOa9}ut9iJ;vqp2XF&}V$3LLiNC&8x@rJIfW`c5` z>|uriGq=%^AHn(le!^T(!6!le=1?Z8DfNMRKx^4U+;ZWZ9}69c^ov(asU-!n3(Aq2 zxUQdZ2{a0kVQm3EJS7suE20_k7jDBA1FbaQSX94G^G&IjSCMp0Er>skR0}=x?-I^& z2>TG=PaP^nmIODjl77ten2natMU>x+v6itxGU?8IN`Ceq>xL3Kb|&M+J3q+IkP7RMhF zRn7Ixg+vv?iC+L(CSW>ksHR)0zM=Rb4W-d!lh~@TBzQtx&);SnFfiSi`X70N)Kk}~ z$8>r2H^`t8fjyy?pbN99Pm&w*bpWG3>uZ4?Qvtn*x$#>>9dl=JLIa3gJb|`DIzfE-G~J4NOdchDzFDgh;J81~xk=*)!!jO*Q>~<45Qb zG7`(fPZCYd)6H$oH;7mGGK@x~@LS_>{S%E$RZ3o00^a5r-ycZ?m-yQYJ-Jg%Z@Mpa zn_Ng92RA^_y%?5#&u7GuK}F_~EB=3m)xWp&LrT@S#r)`K3r8{bPbGcPs&ZT1ps z;w6@Y=EBuYFZ9*56IC1KM}-CY zC=2cg{}ro~ES)ifiNibfBAv?E70N}-*nE6Fh`-sq$PAks5hi>&dJbN1s&3e={i%{D zP-)S0t;B%niBR#tTj2p<(d+4ds5|5caw7SiJWjo(yRoJDrsAGJ)i4u%n#fC^lx|l{ zQdiLB84OS{ghU77^NA+r{pLO9y5=cF7l08egg0$9sC4br6BG+ThVBM_u{=CEU=dSX z74{sxlDbd!C3}&V$SKr%TE)KR3WU0WN1^%AT%ffZORFg$^=s{L!y}U$zJWHu#}f6; z|Cz6t2bzx(>+qsjKY&4T!$jRx^)Cf4jieRH60rf{hk=1&4&R@BNAIPclHJJ0xZzRQIphR1 z!1xvLxQ@yJvf-KW$)$itTR{cP1S~WR5c)OQo~%HQC1DB&dbliCUYO|j2M~%AZKnKC4O(*pkuw&dv-c-^e)h+%{KMtIl7L1p2lg&vb z_yiZpJ(QbX#7^P=6N?3(hj+(TCYNQF%4REjYaso0qZ?w8W0;=6&9}^QiyKtge4-J4 z1icO~GvS7@+TE%%^7E2&scZ3Xk*v^qe?y@vH-VAU0T4gzGm^{6;naS*7Hi|%idO^u zfn#g~ex(f@KuCQ>*T%RXx`fQZI9v(hBNnM;jX954hzEgV<}=L!NlGV7hOSQRi@p!l47?BybI+J&G(oi? zfBHWAvdJjvptm!lKo_I@i$fiwbrRX>hSJ80D(bZMkO6@jB1N#%cs_B(T+UM4^3zwpP&MY^Of0^bW^Gq`9I%(KE}6++)YWD+w5~b%YQmJKGGxJJvBh`w|t1I zsWz&gWx5StLx& z@9WIFFC)o^m0jTniW*^W2bKpg24!(of zX|89PVrgZ$WWGy`#qXl`;8~`iK1bVIl`C&AX_%@X?;2SXOo&JM?d*4YDm9jT>{|jp zL2c0KC(;%c=6i^r1M9*QW8;%^GOJ}9l=C#LfEQE;ZjZwFTw;y6zGabRxTU}xCobYB zmWBKRp85&xGt~?EEy=0W;rOM9FWAA)@NZcavyqxbKJrcTt@de2lB@uD#AQwp#s-Rk zsaPVR%Q$6aly(iJTV)g>1a)BpiFxK6%SOvWi_uck9L0xXGm&P{GlNxEU)@+yP3lNP z30t&9XrI5PVBl&o=c!fX6W>VRMBgvpdD2ZyXZivrEf1Uzt&T2FYy%kz-WjT1qpM>) z0ewO)V~vTi<~o+W;9s_7w7CIs0q~T=Pz~cU-D&j&#UbD}%}&gZ9t;KibA-0sQ06_g zmwe&t@9X8e?3+yc1k8?qR45`D}yEXOT* zmI0RipoS%QMN|dvGKPR3S4L@;rP5@=6P1PA2A&AZxh+hXx=6nD_3$vKIHZ*)7K#0#kg*oZxY_;Ic8B>GQ@WL3;GxyV|uHXYfY+*tRV9^c{TPZ z90x4Oz-O2iw2O54+W2trztC4eHltIFjvp*W0}sP_u_sAaMh1AU7WnB?O>bd0dJH$2 z&6aDHkmVVu;s)jdyfRiAd1q>5n5JE%nkw%l$xfAwmyh%eUJ|?Vb=V2Cgc5wMd@^5I z-zDEsGDaO|PH_ogRiInAd8})4VrGNvnDU5buD+yc6?_Vvjr$3W<%UINB`rfOGtCu= zf3ZbKUFf92tBrsS*XNSssde$Skqbe!e+NIA-9;CnGQO5RzgOd1>Kj8|pn5W$xh+D` zz@N~o=#NAqT@)y>#+st~*T&}XB(xL$j)@>vG<{n7Qt?@R zS2xQTgKDBh@biSnoM$O+En&TGp#Vyk#_OXpc#-j$?vMH-m}{>|k4f~8P7Gb}R~8sH zK@Xxzk?nooyl=fVd~sh{>LR_3eaW}>r-SZ@5Qo$Cr2Q4+)dO`Ufzl2l47QedYrbHq zXsvH8uoSn%iD~#&kU^X=N8eM^L0M5I&3sQhiM|6$^&g=!SA|(Z<&fQcZ@t&Ndfy|T ziyTHb0vYr}TpOGe`6s?J^+>|X73!4sxnUr50lA8e1RdZ%OHFHctH#>X($M?>=h3Hd zF6ffaHTRVVWYaS3lhtFj!$Sj4g^8fTPEp;+zkJWV2fc3Z2Hzpl47!Y(pDbntlVN$h zWU7l~j(mq|hjxMihQ=fRf*yase85uAI?`I%y4*6?3=^%f3djSK!_Y`uPi2$SnOn)d zv7O;N0h@RU^stWr;fDJjdDnXHdk6Z4k|3F7!%$l&=m| zKzrD_!fXHj(BA0H1e-1al-Mv$Pd#DU2ERkE<892-!SnC3uCWr z-Cp$qMQ5osO~&5>C28@mQe&uA`T;Z~4LgN};mR=80(_ zLw_ma>Y(nHu|2#A^vE*ip_Ux$G3yCy6YDrjE%Q$N7&-+0WHjjsO-%6w%;o<|)Q>g_ zP4Iu`*8-NykjH(`ygj__yeGXgd?(0;bVYUy9}uqv&qiLvlc_q=af)^74Z2~*G}I04 zh0DyHEVZoXtq-iDtcNZC0E<(OJ%yW_{?)J6Ojb6LDKh^{90M5r%wIux!#<)>>bCC< zh+f^h$lKI6oMfoijEY|>whGpbbd4`c-Ic`UCBQ^RYdiy)&|>&^BHL2Qdd>RXy59N? z+;9V809F|}2fCzOODnI*W@nlt@t8VXIxt(nxdfdt(CSnl`T_u3{SyCp5w^OFK+e zQy$1%N-m0x4=)V-PxuS;uwj(n=khl8s=PVguigS*XUf4e;?4+z1MR~DW9yS|GKjpT zY5-s{GZPP(nNe zSmJsLBK_Wm-k`_oJ?=f^lLNot2isiW{U1W^s5#j?vs?C28Pah2i>A8B6fBp>H><4= zt+*`;a&(TRgn1<16ZHe0^|!8%x}3rz*`FE~ZxU%9oGG$Ehuxv7l2Ttougeql%<_)# zT_eZQv)QlwJpX{uSm4(DOqZ4oRj$;m*7q~9aBZwSanBsK+_#prS!{c(S1n`BxA7}z zKlq06m+r0l5O9Mm=}+-fk)y%4Kxfd5Ky@W?Ut{kl&v#EpZw22((n>qniGZnlgZ@ad z#88l-f})6~oL*+y2ZJwx;(N>mmOIvJwwktQRzF}uC_!M4;EJXm`u3V4%J0&3>2`oc z=t33!%K?ikMNcEk`C52icR z-8RJ9*pf%Q!8Rc}s4>7eLiJ9zFjFfTiGB&W{S}2P><0P~nd|G}z2RBsdGA^5-RCP# z$(S7Oj<77SBz!t1B%4Y$$lt1hTH0_DDuZ?d6};1O)Y`^2+t$Rk)wtC&y;=)^veMeYw%@kc7Prc*2hG3n`)D_KxA8ySN_87WP_jGKDQ=DEf>p&WTq~v%b)}1Wy>-bDWn^da6~I)fZuFnTt8`h}OyzmaL;Yb>3uGs@lW1U>37-F~?U1dU zt%;R2*C9$_w_(UsQm@iHR!owb(+}cHBNKuv#X@cwvw-^K+u+UfwDWZG-18jta=yLP zNyfnEi3fr=BZ+tykfAc*7Z1?4HHF|l*a*U68D?#5yJ@>?>uH;9tz%h3EXB$r3r)N9 z3pFj2oOFGoW5} zd>Hx(k{csnrZHNsk{nESh~Z&Xpt^8?9YoI}L*B2R3LeH?%=6Im$=j8zN{?jy{C)q6 zkSx|Kc_UL?{*UU4_Nn0zl!N|@PcSDf#cZE#S$4mzm#w|^vpIvmKwH7Hj8k-t)RcU& z#F@Gm`!_r~uu@>zz4QryKt4}7&o_6Ru33=2k1cS}85A(w7X)rkw?HvyMWpZi6B zBWwF&p7Ne&?jP>Ko}S*zzFE{B2IoJDAA*W#uf(l%1=$+q7fnjfnf4%3+-SaLNn1bJ z8rf^w^K8$plPnjBtyoE9glV+Cfre5nk`n2&@j;O~!KUIWt~g_)a(xDGCC^RwZFdci z#XAaML_OvxH%QD4E{S}KHv}0%Y zNYD7KR8{G2MNnN)U)>agXJ7}2v6gq%m$shvv3AJb)TXiyHunV?)EOFR$k8&&#WEyw zFwruq4@vwr_|wcB>bNh*ThDXMy&Rm-LCu+CduWO%W>u9}VzKM@SA3)y>*RzL4PPI%9OLioy#oVFC{(QbE8=$1*6mMtGZg*ezT6b+v z2k$H2Wh%~W;3xQ3gx*H$f~jPA`DRt2R&De{+ff$xn&(+h*hbjTf=}SKeX{no%p&Sz zU}w&FMK@8cRcw%$Q^#XH!<7Q{g>`IAx;nYZJHWHk-Nrq}E%j)TA6C;!7n8%FzM!0 z@%WSQ=D>F0H#?0UMqc(#1o7*-+qvJkpL(kJl4J$u3^z|)6MPk^nK+fMBs-{-XzLj2 zK~ZEhzR+CHy3{tz{>J{;-p1a?7PC}0Yw?xHZPNw)6b+=@BE{3Y;`JiwKt!m{U8A>? z-@J=Gd)yV=72OBiD?It$gXBZH5vLNNV28-rcm<&4%PQAsUh2P^jv%G+4ra_c$u`LT z!|t+Aw{Nkvw5~Ewz~e|ZRLv05Y*Jcfo73jRnaJp1A8`qnq2B!<8^{ZR@Rt=Jz0j zPD7^+)3s*RZW)o;lqePb9K0_6;My=+s-o|b=cJo(2VHI5D7azKm!Jl-Rsj#R4Iht{ zNc|_NqBx-T>u^&P-h_Q6?pPYzy4ZQU)$!7#c z*4Qp$qostcg*|Am>ImAK+l$)PS+)_)u!AC#r;V>3wVbKUJN&Ie`=Ykw;Y>C8Srwvd4U}*U?MDo;$ZXm6h`qVP z=9p<8W-G8nh|`!6DQQaR4yh|B_DbN?+F1E82fU!F>?11AXY#&tYup!IA6?_!vpfK? z$s)`(ZlCxl=!kAk6w92G)l%)%{xrCuu-_`k?r z(;R(S%~?fB>4uapz9-x#&`$W5)zGxBhS%lRx(~T7xf;0ZdX9K!lb7gLTq&_daBIW_ zc7v+Ot}4rGCmWVPJ<*SN+8CL#@q(?7T0E%-W_%K@)jjK(C^v*ga-jMvOG=z$391SQKK}Jge2%% z{DFCkHDMF&lO3}i21i-@TdT?9#fPJtpt*)>+MCKMvaM-l0{re)&?Hvmw$gu*3%sxg zb+2&EaQ$*!cWb>5eQs(9a0{9T)`X?;b*XZ|&8n~2s=s48fwaU|n8#QPZIpeEW2>W{ zW0*b5Hqz3G_=Fnal;N_rnd*|vo>`obMGpq2h>N*=dJB2dTft*@FL3pDU2x3JS(P29g@^n+*6m)|7Gfo#ITO$#@45{Kla6rbB>{oefGa?_beBPR@f4F zuCbo(nX0~gUq+dn6U9Q`#NS+H<~``~tv#jP|F~MXR=8TYr+ILnlUl?y=3DregaXlj zlSL%g<*n5hb$(+2K8cyku=PLN2m1=g6UQpY7yDV8#cDQR!cuV3cuUts{Xkw?vJCLt zrJ?5j`uto5qcpw|o|^7yuBxsfE~C4$=eGA6Nia{iN1_xcjaXuPrmpLTCIt`BzL&s=jZT zr@4EI%i(J1`t0(#M|=B_$LX$IYjG}cQzj+g%q3Yz)pM=Wmv}sQI9%DP0Sc*yOMykl;(OJE@7jQ=VS#sV+VE3x{1t+*WVY z*MR=cJ`-Yr0TEw(MY@{om9m@mjNv_W32jbHvoy8sx36@h9AU>a#~eFst7D1b1JUVF zTZ2c_Pk94m=;SyRUJ)1|%w+SaW4?EuG47eJWMQIkifbr9>7BlJ)CjhNFg@@oJTNW+ zQ_qgdubRe&2~a;YjyJH_Y%Amf>LWj%i6#0){{%OS%ecpMf3lbNhI^wczwp08uJDwrz`eq| zi-ef3949sn-H#4U;*v-5;cB-o+tdz0K@Zztt!@9?QNlUIS;jfh(cFH>y3JexABJ={ z$@PoW1@h{WdC70lUVx=|I196nT<-nu-tYQZxVi9g;Z)ZScMWegxtAWujTNs4>qfsM zR%IFkKX-!exsihJU`@;et+>69qm*;Hv!nC4W2HT2O_+D!zmN~6m3pIQoZ`Afk@_q4 zztA{;Yko9SNZ#p`h4zGC!#3d7w3Mz&eUG&Y|L~vW?=lrAzqh34 zA$a}?h4TuvE|Yt?XR_}eHJ%+KoC=hT+>1|7*O0lCbF_@1B#Z;6!*6+Rs}9a*yK|!x zc4CeLwmX(-#4Ge8w9QaLyF~d0WaxnSy>Q0>ELhnophvd%d~^LN99%e{@N?lySGEW7 z^`s&U&6f%62-l9&sT0ycN~w01;U;tg9Z6iZ?6=t*SZ9 zjHk4Ss;_K!x**;tayKwjn8MzsM);^*$Yn62BZ^2y3%{!ZMC z)DGqguUP|q#COmGxrM?Gg{2B-7j|@Q2VGzRX<#_c0Wi)PdzaiI>7`I;*6T@=41I~$ zx3sbq+I7yWPM>p^^Q~j8{j>FwxeDGDDQmi`tDv4HKbK*_%4(^1#6v+$UiusyJ+9Jid3tVd3R z^Q*nO&1pG^zd}x#+UfJuazz8lwB)U5sn88^9e0i{M=HIY+HrO*3H z(?J#mZig&-B{4VCS}s%X)0s?-kuvyYbH4R}ops!G5?PG1rL%-%rEQ@FC)%Oapbz>M znhlC)l610hY;UN7Kfo!O@nnv7lH2KOQkY*LE8Jgr-PPXH!FP%Jhg~ap1Jfeq5<>cx zY`&_LZjbRh{2Ci?K53m}{|@4p&4ROLJ4ZSG*gTfS#3S?~)Zg$09dE{j8Hg`2wv%)t8zY4~H8Vk97-WF7v(F+3tzVMFt&~!~O6Fa7@V4Mn1$4Z$e zTYK6cf%B=8RWs|X^Ngdmy{$D!G{&-FuVJ9}jM5Dvqe_7tUpfC zvCRITb%^;Yb`_prWV8)cQ)K(nZ{n87(m(|PV>?r%mvIkrbu7G6u&+Q<$QO2Tm+_7S z8KmYr`2QE09c!6#NEyX(fYJM*+vo}+YDw8PI?g&fXHCe0vZ_13*mbto=4yC7Bx?Le z_dk_fUMbTju|1LsY!xQ6`>66hyJwE8SK;Y`MFrmq&J{}CPdrKAW_l_2P;3&SqnDFw zB*PSCHIMZ*p#f-JBG2;5HqCL^**j}xR)eew&N7a_Z8?@>_;qA~Nu}?v-Xy=C$xk?< zGlB~7Ju6aUKn3q|4JkZYFuLGO!IZ*_uF;+iz9O`WYam_>_KlWKW-{O8htwVP4^0Wg zjZd-cvh{Fmb@t0Tm^C8nq;rbnf$fmRN#vkvXp^2*mr}Hqj7{!~f?o_0>u{~;bH4eW zTdv84hYGqEECDz0w+r%=^PQlUv6qE1!55LuiHVs`@}lZDx^AXz$XdLzWduN=rOp9a zd0A_-+|FB$EW5$Ffw+pUg^C&GYAypuJ(?^Pn-KDfyScq|#CO?~?^;xNprCQVz=BLc z5!V&>J#TGF%T^V32I@y-i9hN4vhAvFy5GiXNH*>=SF!0GQ$YOZSr@aMSt{pr`(&%a zoQ>h|DT7hlS~*I(By}>zfr*cTkI+p?-lKJI2hU%npnk!#g3pBm-F>|e$kogV-V`_) zo*3_(t|dcM@3fxDc)`E?NI(Nj=4RoHV3(QY) zly{)}dEwatydaT3te|sYo@)hVLYX!Q3U-{Vu zP~iwyZO>Gnopy3##CY&pbbE4%WRjw-Cas?bdcZNFmbDK+(56|jtg1!wvyiO&j)d*L zr36t1{buT<-=MxAe~|f@h(($NuM0ES#T4hg=T^HO7bprIfiH8uDKNV}xi!9n)DHHK zFgjQ!sz?SiF8KrXzj_1I8|_N)meO{gqh?k*t921Wkxp5#GuK|lx|2AL4u^i}OKDmu zdP;^S|Bc=bRu+G-->KTZu)DnLV}YdLO#Ywz%>~a2|8ei}R-i0wFX3BYQ)FUdaHf~M zzFMZ=VG@uSe$2wz9y&^7Ns9C{tbWc*_U%^0Tpr_~@rFFjH-%5)PikY0LI=f` zTrGN@uZpL$%Ln4`%)gr7w_tu@5qC}RC2}kCKfWDsjK9ZUr60>KsQ%TJ{C`7N!QjNz zMU!ljO|o&xd5EbSI|SllVaU5hQYk;%-&-8Z_) zH-F$}=G}AeIp^lhd))Pc1L7ZL*A)9zTq6D~U?lKBb3)BkkrnHtr$q}1C-5zE7Huhc zRo$-i{e&WVA-LW5(mmC=&|Zg=@eNo7rpMdZp4wkKyLe>&Z$h`CGirNfI^d_0# z(;Stco0unCteCG#s_Qh%fn`8a6Hrf5O;Kc|4a6kD77oIs()koo9?bNp-5FJffKbv`8%ZY6c4f0R(IGyk8&23!#7i=hY z2Rn-^?PTXj0zs{V2O>Qasx-e&K<3iojH~QU{NgKfj6;m>Rb9bVS~?}Y$A zayh;>wK%tow46GV(T4qsrxMAfSLIIS5jCbc2z&uH0@pR;)jO2~<%c8{!nVB1lBdNV zivBCS$bL;S#Fh`_a{PIVk46MvO@}^i$)Z;Ex+-XA}9~L zf|>Ev_9M>b9>_m6l#V{Ey^^_|e@S^wzr>o%wF;P$FEV0IsXnJU4#+_&I2HI-b6T}a z!Io*o7JfNrBlB-oyvr~|kCg#UJ^cyD&{WI_dmG;-!lla;RjSdN zoj^Bm3P=Yvz$Nu_<#hQ`$xjHJ2wl0f3jqk!Pp+BG-(H_`pyoY_MQ}1c+-x>N}Op`QZdlV)X zEhwH+(tvkT_(^h9PFKa%6M;eCWpFcS0*+|{s#^-BOf0_6=W&`d>(f|dSMGhv6qiJ{ z2S$0ENAozs%1qm3@e5?W#1GVo0dA!lmr4WM%AQ=zD(?&lIQ7-Uc_J4D`O0 zihe{J;WYaXPL-#-|6XWyY*})D_HChvYGCNuA>Mq^G3i{z2h~3s4VVG@YlYhWU|!Qi zjVbi9BJpPaOZI!lW$JQL-<&Q5#s`HT`8Rv6I{Vp2;eM2cp0a+m?naH+5u4Mo&;5_D zamW)zYYUl{h3Q2V#rsR<@#vynQiI|z)ea2{%z+cM|I=;)2LjvFLzG8lTg4i|1kNwa z!8A1)%Uw&IioXuG4EQ`nt_}7zI2og&yQ~+iKcJhj?zROEkZ|*Rg5}X3wPQ1X=08v< z#iEipcel_k`6M5qnxu&XS#W`NqV@^64+y9qD7A8lRebOxx{UgT$ z1H9k4p4l(ra;z9#Y29cA(9W10XE=_yPI?)^eaJ_b;HV?XiewwDLz4HB%uEGj#73&ZB4-{$MlD?PtFQN!u@m9H*4g_zBanQNe zpRF!y4Bde5v)6ZNy$b`{2r~hvN7U^jf1zcV5zbkGQqoxdNy*gQ143Yh_KdcbwhTO> z`CBDX=%nw3jd{IFnieN1SMu{S18N6Hw+EFzl6#7=p<`T>t*X1v?<=ge!;ni zFi;!9GvcdKH**D2SK4&uOinAoC2_TEoAR^z5a0k0YF}xmXcvJ6O_6%5;*@kO!IHL; zH^qMyl^57qOYN5^Gqlz>(*38Sr)>@fqob|$tShXO(9hTlTU)2p^MgMUdK<%&irhHT zNve_IWdFY6J`xnq>@IKR}>%z49h3+s-4 zXN@CztIgU3%i{wb0(U1LahhE8NbQ#lT$o#QrT9b1UEUW-)#RT0$tHof-%i&RbvGxy(lD($YR~2&!Nck&j=N)`^ z)7!wc*xn7_Oyu`2aser`c1IDc&bHgR-$M^P4BwB3Ql0B6$$nZXs}+|jTqijrpQt*n znMB~HFEk$VYHeUS&_P{AAm|&>7T)%f@x|hzfAZrpx>|m;eei;JmFvEJK3+xO^D?px z$suX$Dr~z==@fg2o$6t2ynkwME=3wlt6-kxY!s*@<#Ls3j;0rZ|Dn(-2!I-C&k!u2 zQ#O-*7S-dIv1yDOMI#Dznd`Nc(Q84q&+1}1PUEl8dDbdqHu3_wY;8dB{2|9yH|EY9!0)GDv@8TAJ98^eZni1`=UW>l$IQly;_h{r!xLz zZ{>Fs6T5Pi1$7p!| zL2$4P+ig9B{12IqG_fv2hvS#*t6lfKBZ6I`<7&@m1ck*#ABq{oEakbVnXF9tK>ZOI zryU2qg-$~;?K#k_*{HfJ-$3Xr0XM=tPU}Ygl3SAM5^o!x=cjtoj-PDZu`@*cdPp}U zh%`c@SSLHlRpz}PI25^)U}R?IYbfoC7nQ8%4H3PNS`;hPCxO1i^V^`0P#Kg5y8v~n zt_rDii|`_MH>)4rNnTVZNxzHV4txF6J%gM_Y_qY)#QKO41#$^7S+`>^ZOffUJq-e+ zh$1mQ{hUDPe!7!I;?)SpO4lj=r(Og!(~gAVPzIU-_17K(eo;SA9F^)sgLv&rBJ|A^ zW&TonLgJgq#K0%dNvF$p7(=b65gHP=EJC(fn`0wv1&7SD(?2f!Yy5T!uG>u}(}%Ja zamNS~lE#XJswW`O4uQz920jK=Y6}3O?n)r&CgCM51PhlH-N_Hkq=+7k2D*DGu4eYn zSiyP)Nn5Hd-H;yEYSf3XbZmC#e2+r!W0KU8T!b{9c83|@V1jj$xAHT@B^_a~7sQ4; z!8H)B9R>E++)@538zkOB^tjH9uSMevfy^JZ#Odb2%ieLWC3X%Dq4$xn<*J2)WRXSa z?>I41b5HUaLQP}8BtK^RlO9nG%(0wt0;yz{e2nU|<~7(362T+k61YBe4g5#bUe!Zx z7dH^-IZnn3YB6bRwqDW|MT2_ZL)S}tAG|mE4)IzlEf&i)1VlynCi`O72k(a9rs%WU z#@Q1En7W1Wo{jL2h}m+kYLDhPSW0Z4oey_}XF?n;1YB0Vk}s4z6+Gu`C3NCBQnTEH zASi3q?$S*I76nABh;5R0m z{4M1u%@R-uDd5fU6!<(eUb_Kks2;3fNyiJPb6c_A(}s~hD@#e758R?~16i>}RE1GXP9$W$;0G z2ONN|YJxM0+0=pf-0&uU(*4}Qvb{pZR=?#3%P332qO)RX zsm<-sc&_;mhu_7^(iaGXUZcxO`tsU|s--2$4{9}tXa(>^_#e1FOohrpv1Wv_k*rd5 zk+-^pxEYV4&hJULO4Nmm0~0*3bBHaEmRVz#zLqwYTb57Aa`Xy5iReKpe<9Q$zCJ~( z+d-DlSFrAI{}ui$y`b2nb_4gdO!zKrhR4H0piAI7jfp@|3-J(sBler(@}fuij+xhq zO_6nhYR?MiK3hv{u(i(8$s#6JXak}}yW!}*Vj_Be-#>2FEH4gGC_%CqkO-r9Z;?{Lm%N*x-8rfeyn{9e5W3xNK0A? zwcNMNA+)=sdbvZ%&M{G_%y-(g)$YcgSeqe@EceYp^HmGqnnQoFjdtGlEDdalm=ix_ z0{L}Ce8wVn6@RA~lJ`+1H3scd=o8#u*FZNDRzdB-w`!N-59vqYGwyG!0x_@rFV{6y z9orW=>l3@}_J(-W+6!rHIc_QXxCr3Sx#qSK4CC;R~<*k(2#aIc*mx`~; zLdq+eTJRk70UoOxsyheIf)0VbHM5jD**wuqo}uI{T|)V_4ogjs*9$lBuW)yE%*J)- zSfsh-FZ18#)|M$q6LcA&Q+ByK`@4qM#q+7%b!{kb=zU9e@+w7rWfjU9n#bS{=s$Rl z?l+wWegM(5cQgrt#fU$g`0q+a6hEYZ`88=;;#&BWKjhx)xQP!(e@EI{Hkjv`(`GsH z(#pVJ*van0zC9sRtUTq)Z6P8&}`e#gGhJFQuAQ*1M^EF!@IFw_Sr6)FA;1W zJCbal!$@ms0@hq^1yKPX6(7~Zz(LRrc%$yVZj|mv_?h+?kX3z^_mzwhlya^y0P3%W zH<`}0Un3U+pFLxo-`Sp{kCAfAJad_Ovw69NWz}PkZK!jqw>-Eu8n4})ZB0U`^Oy*S zD-23zD^{x&U<2qByj_Rr{?HwV4UiaIs-CQ{ODMwkoC!?geqquALg&tiwh0dOK60M4 z(J>z~&N9<1G5=t0YdMEpLwnkWINy5C28@xO#5{3Bp$)Z~(U-GU@T-KOXr?v-1?_IQ zLYE@mzz?s0R)HCHLh-xwxNr$q$XZ5wO9FGhCyQeDgU`KsHd;eHO#&l>uKF)nPGOCa;6LBItya$hj(!7cTe`O z4ZGt@(hc(%WlOP)J(IsgTqa+p8Uh^A{tWNb>GcZz99>iRdu>!xqB<}0ir(^Ommu_} z1VW#t+QvbJVq#H?UR ziG`OD-IswX1~5z82i~GP2onxhvhm>c?9k8X&u;ysc@c ztq-r(_0Uh!dv)(&7{UOhx<=ku0t^0QcVlcXdY6}E<|PW@|NH^>JjZn0Vf9*8n{Svl znlh$hBExp{sLkSB>)jMIMJFX?xf;@6v`(z++_3PAbQDoxd5{V(&<)hD*TZ^Hw;K8$ zJfxnfcqMr$Sjvepx>J`I?qTlX^nrjYONQBq8>FDF>5Lh0e)LzOgEOezN z8S6ME0WR63c%)tqUWSI~M(7{tH|j6zy29)KgsVZ8qDyq2M8ugG>U2J zj^G-oBN4ww|BpVY+XJ7_>Ht7>M0QWKj>jq)O|Kw7A)MI6ST<)+fZnEWuaCe2 z=rC|b)k*%dxE23)iLkg2Wq#e66eYej^pkIk3%0kwo+Cl?ITOpoFr75n&Ho}W#4ftck9p&d$rvLyd(v|D;dbVMazL)MdNDa18e4orh+by6s;|_c&~EU)`WMAH$yNcxS<1Lv^d(Qu^i2E{p6Orc zcH7@z?W|>%2d1F$jzNa%j?Am=lvR^5)JM#5+y}yU(j^3kP19Du_4R;Zu0dyLsXq>{ z(7w_9t&B(=!gX9X6Qp$^jmhq)4G=12mgkmZ5q`tE(n5@zj4O(v63zfxD_Qd3&*!H>_kE{S5hj&Yo--s|wEb z?r}D=^*~XJ(2N?(jT4P3(|q$}#E+(JD_nDZcSGfIX*yl!q&zKN%jO9BNt!4Qt1Ce| zjOqZxzlO^Oy5Xko9W)disD2~I#e4ZB?B2!W350G>y^eJbrMz6%c7h|tNHep;*vZ)5 zSYuS1vzA}bX*QelrI!=h6C0iCR@Z`}D8@?`@*j(D$h)Zff$yLPI+@|M!DQ%V;ORTS z??H=tx?+>0zu*Cz#84GA$ahZ9iGL2Q^sRNpZDzDTGRPb>HZcl}dyUUbJ1h~ag+LJO z{V`~Z9!qY_EhA5(|E~n$br5%!*Qm0eiddF}@&(uBD#x;kOU!-S*O}X=!+o*pQ4rcBp*pWT{ zrS2#8HP|y`ubF9*8(-JdGnSg3nhVHtOzimVuJbR9G_MtBxg-Y7&pgfrMBm70s=7*S1=^{W%0`Op+$OBPw1K1{*=et$cMFFZV_Bb?)h3bg zLQSN`Xq;%?h;+r4*;}{=`5%NABt~S06~3c(Wm36Ygb$^EDr+?#wK`pmK4|!zn z!)x6os0e(c(#o0QJvF2;CHQqWndS5|7aXru6bBL2eCA4+!_8tQfM25^GA0mWI@A_$#=YN%$Z;%% z*10M6;ydiIg6onCinf|=+Pm;Ez1Og%bZzMu!y5fAn5C^%Pf(1PPz67-mlgj`Aas7} z&sZS%hj*(pi#x4jEHg|BA>hv)FfY;?i}9`Qu>Nr<7{ow zzbso#uyIsPr<&(AY2zz%TWcACp#7deU~6=8a&m4Vc?EqY(W4KFZ^@^t4}tyQar&=@ zGo_zPhnJQars)`v6gZ;1DV-^N$4N0#MXtOt?T(j*t9)x*FKwIAkCw}(rp7@v@|w*x z$Bp9&7C4IW9Z`1&qQ_O$R%LIKUeg}3Ht~4k|H(<}Z$UQPLI2Wly)<3AvviH&gKiCU z4Ct&HFVl)Paj!A2Q;!t>$W$brhCBGF?zVOul^`Zl7h~_5 Date: Fri, 5 May 2023 07:54:43 +0200 Subject: [PATCH 05/53] Corrected tolerance level for bs1770demo rms test --- src/bs1770demo/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bs1770demo/CMakeLists.txt b/src/bs1770demo/CMakeLists.txt index 15a8b7f3..daba5f89 100644 --- a/src/bs1770demo/CMakeLists.txt +++ b/src/bs1770demo/CMakeLists.txt @@ -16,4 +16,4 @@ add_test(bs1770demo4 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/bs1770demo -lev -26 test_ add_test(bs1770demo4-verify ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/signal-diff -equiv 115 -q test_data/sine_ramp.26LKFS.test.pcm test_data/sine_ramp.26LKFS.pcm) add_test(bs1770demo5 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/bs1770demo -rms -lev -26 test_data/sine_ramp.pcm test_data/sine_ramp.26LKFSrms.test.pcm) -add_test(bs1770demo5-verify ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/signal-diff -equiv 115 -q test_data/sine_ramp.26LKFSrms.test.pcm test_data/sine_ramp.26LKFSrms.pcm) +add_test(bs1770demo5-verify ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/signal-diff -equiv 206 -q test_data/sine_ramp.26LKFSrms.test.pcm test_data/sine_ramp.26LKFSrms.pcm) From b5a3a1ca49ca62d09af153e11b882d08daa9c488 Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Fri, 5 May 2023 08:23:38 +0200 Subject: [PATCH 06/53] Added bs1770demo -rms option to manual --- doc/manual/bs1770demo.tex | 7 +++++++ src/bs1770demo/bs1770demo.c | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/manual/bs1770demo.tex b/doc/manual/bs1770demo.tex index 2a2bcbd2..1261df30 100644 --- a/doc/manual/bs1770demo.tex +++ b/doc/manual/bs1770demo.tex @@ -103,12 +103,18 @@ \section{Description of the Algorithm} \texttt{fabs( 1.0 - fac / last\_fac ) \textless RELATIVE\_DIFF}, where \texttt{RELATIVE\_DIFF = 0.0001} or when the maximum number of iterations \texttt{MAX\_ITERATIONS = 10} has been reached. +When measuring the loudness of signals with a large dynamic range, it may be desirable to disable the gating function +to obtain the level considering the full signal. This is useful e.g. for noise level adjustment in listening tests +including added background noise. For this reason, the gating function may be disabled using an RMS (Root-Mean-Square) +energy measurement option \texttt{-rms}. Note that this feature is not part of \cite{BS1770}. + {\tt\small \begin{verbatim} double find_scaling_factor( /* o: scaling factor */ const double *gating_block_energy, /* i: gating_block_energy */ const long n_gating_blocks, /* i: Number of gating blocks */ const double lev, /* i: Target level */ + const short rms_flag, /* i: Flag for RMS (no gating) */ double *lev_input, /* o: Input level */ double *lev_obtained /* o: Obtained level */ ) @@ -158,6 +164,7 @@ \section{Usage of bs1770demo} Options: -nchan N Number of channels [1..24] (Default: 1) -lev L Target level LKFS (Default: -26) +-rms Disable gating (for background noise level measurement) -conf xxxx Configuration string: '1' ldspk pos within |elev| < 30 deg, 60 deg <= |azim| <= 120 deg 'L' LFE channel (weight zero) diff --git a/src/bs1770demo/bs1770demo.c b/src/bs1770demo/bs1770demo.c index 511ae71b..9712c123 100644 --- a/src/bs1770demo/bs1770demo.c +++ b/src/bs1770demo/bs1770demo.c @@ -282,7 +282,7 @@ double gated_loudness( /* o: gated loudness */ const long n_gating_blocks, /* i: Number of gating blocks */ #ifdef BS1770DEMO_UPDATE const double threshold, /* i: LKFS threshold */ - const short rms_flag /* i: Flag for RMS option (disabled gating) */ + const short rms_flag /* i: Flag for RMS (no gating) */ #else const double threshold /* i: LKFS threshold */ #endif @@ -324,7 +324,7 @@ double gated_loudness_adaptive( /* o: gated loudness, using adaptive thr const double fac, /* i: Scaling factor */ #ifdef BS1770DEMO_UPDATE const long n_gating_blocks, /* i: Number of gating blocks */ - const short rms_flag /* i: Flag for RMS option (disabled gating) */ + const short rms_flag /* i: Flag for RMS (no gating) */ #else const long n_gating_blocks /* i: Number of gating blocks */ #endif @@ -357,7 +357,7 @@ double find_scaling_factor( /* o: scaling factor */ const long n_gating_blocks, /* i: Number of gating blocks */ const double lev, /* i: Target level */ #ifdef BS1770DEMO_UPDATE - const short rms_flag, /* i: Flag for RMS option (disabled gating) */ + const short rms_flag, /* i: Flag for RMS (no gating) */ #endif double *lev_input, /* o: Input level */ double *lev_obtained /* o: Obtained level */ From bb40db38322693de6aa6a241d474418d9ffa3a35 Mon Sep 17 00:00:00 2001 From: Erik Norvell Date: Fri, 5 May 2023 09:38:01 +0200 Subject: [PATCH 07/53] Remove define BS1770DEMO_UPDATE --- src/bs1770demo/bs1770demo.c | 62 ------------------------------------- 1 file changed, 62 deletions(-) diff --git a/src/bs1770demo/bs1770demo.c b/src/bs1770demo/bs1770demo.c index 9712c123..84ec2f77 100644 --- a/src/bs1770demo/bs1770demo.c +++ b/src/bs1770demo/bs1770demo.c @@ -11,8 +11,6 @@ #include #include -#define BS1770DEMO_UPDATE - #define BLOCK_SIZE 19200 /* 400 ms in 48000 Hz sample rate */ #define STEP_SIZE 4800 /* 100 ms in 48000 Hz sample rate (75% overlap of 400 ms gating blocks) */ #define LKFS_OFFSET (-0.691) @@ -21,10 +19,8 @@ #define MAX_ITERATIONS 10 #define RELATIVE_DIFF 0.0001 #define MAX_CH_NUMBER 24 -#ifdef BS1770DEMO_UPDATE #define ZERO_BLOCKS (1000.0f) /* Constant to signal that zero blocks passed the gating threshold. (Only results within [-Inf,LKFS_OFFSET] are valid) */ -#endif /* Channel weights for default channel ordering. Assumes channels are ordered as in 22.2 WAVE files: @@ -106,9 +102,7 @@ void usage() fprintf( stdout, "Options:\n" ); fprintf( stdout, "-nchan N Number of channels [1..24] (Default: 1)\n" ); fprintf( stdout, "-lev L Target level LKFS (Default: -26)\n" ); -#ifdef BS1770DEMO_UPDATE fprintf( stdout, "-rms Disable gating (for background noise level measurement)\n" ); -#endif fprintf( stdout, "-conf xxxx Configuration string:\n") ; fprintf( stdout, " '1' ldspk pos within |elev| < 30 deg, 60 deg <= |azim| <= 120 deg\n" ); fprintf( stdout, " 'L' LFE channel (weight zero)\n" ); @@ -280,12 +274,8 @@ double gated_loudness( /* o: gated loudness */ const double *gating_block_energy, /* i: gating_block_energy */ const double fac, /* i: Scaling factor */ const long n_gating_blocks, /* i: Number of gating blocks */ -#ifdef BS1770DEMO_UPDATE const double threshold, /* i: LKFS threshold */ const short rms_flag /* i: Flag for RMS (no gating) */ -#else - const double threshold /* i: LKFS threshold */ -#endif ) { long i; @@ -294,18 +284,13 @@ double gated_loudness( /* o: gated loudness */ count = 0; for( i = 0; i < n_gating_blocks; i++ ) { -#ifdef BS1770DEMO_UPDATE if( ( (LKFS_OFFSET + 10 * log10( gating_block_energy[i] * fac * fac )) > threshold ) || rms_flag ) -#else - if( (LKFS_OFFSET + 10 * log10( gating_block_energy[i] * fac * fac )) > threshold ) -#endif { energy += gating_block_energy[i] * fac * fac; count++; } } -#ifdef BS1770DEMO_UPDATE if ( count == 0 ) { return ZERO_BLOCKS; /* Send invalid value to indicate that zero blocks were above threshold */ @@ -314,40 +299,25 @@ double gated_loudness( /* o: gated loudness */ { return LKFS_OFFSET + 10 * log10(energy / count); } -#else - return LKFS_OFFSET + 10 * log10( energy / count ); -#endif } double gated_loudness_adaptive( /* o: gated loudness, using adaptive threshold */ const double *gating_block_energy, /* i: gating_block_energy */ const double fac, /* i: Scaling factor */ -#ifdef BS1770DEMO_UPDATE const long n_gating_blocks, /* i: Number of gating blocks */ const short rms_flag /* i: Flag for RMS (no gating) */ -#else - const long n_gating_blocks /* i: Number of gating blocks */ -#endif ) { double relative_threshold; double gated_loudness_final; /* Find scaling factor */ -#ifdef BS1770DEMO_UPDATE relative_threshold = gated_loudness( gating_block_energy, fac, n_gating_blocks, ABSOLUTE_THRESHOLD, rms_flag ) + RELATIVE_THRESHOLD_OFFSET; -#else - relative_threshold = gated_loudness( gating_block_energy, fac, n_gating_blocks, ABSOLUTE_THRESHOLD ) + RELATIVE_THRESHOLD_OFFSET; -#endif if( ABSOLUTE_THRESHOLD > relative_threshold ) { relative_threshold = ABSOLUTE_THRESHOLD; } -#ifdef BS1770DEMO_UPDATE gated_loudness_final = gated_loudness( gating_block_energy, fac, n_gating_blocks, relative_threshold, rms_flag ); -#else - gated_loudness_final = gated_loudness( gating_block_energy, fac, n_gating_blocks, relative_threshold ); -#endif return gated_loudness_final; } @@ -356,9 +326,7 @@ double find_scaling_factor( /* o: scaling factor */ const double *gating_block_energy, /* i: gating_block_energy */ const long n_gating_blocks, /* i: Number of gating blocks */ const double lev, /* i: Target level */ -#ifdef BS1770DEMO_UPDATE const short rms_flag, /* i: Flag for RMS (no gating) */ -#endif double *lev_input, /* o: Input level */ double *lev_obtained /* o: Obtained level */ ) @@ -374,11 +342,7 @@ double find_scaling_factor( /* o: scaling factor */ while( (fabs( 1.0 - fac / last_fac ) > RELATIVE_DIFF) && (itr < MAX_ITERATIONS) ) { /* Find scaling factor */ -#ifdef BS1770DEMO_UPDATE gated_loudness_final = gated_loudness_adaptive( gating_block_energy, fac, n_gating_blocks, rms_flag ); -#else - gated_loudness_final = gated_loudness_adaptive( gating_block_energy, fac, n_gating_blocks ); -#endif last_fac = fac; fac *= pow( 10.0, (lev - gated_loudness_final) / 20.0 ); if (itr == 0 ) @@ -446,19 +410,15 @@ int main(int argc, char **argv ) double fac; double G[MAX_CH_NUMBER]; short zero_input_flag; -#ifdef BS1770DEMO_UPDATE short zero_blocks_flag; short rms_flag; -#endif lev_target = -26; /* Default target level */ i = 1; conf = NULL; nchan = -1; zero_input_flag = 1; -#ifdef BS1770DEMO_UPDATE rms_flag = 0; -#endif /* Command line parsing */ if( argc == 1 ) @@ -492,13 +452,11 @@ int main(int argc, char **argv ) } i += 2; } -#ifdef BS1770DEMO_UPDATE else if( strcmp( argv[i], "-rms" ) == 0 ) { rms_flag = 1; i += 1; } -#endif else if( strcmp( argv[i], "-conf" ) == 0 ) { conf = argv[i + 1]; @@ -637,16 +595,10 @@ int main(int argc, char **argv ) } } -#ifdef BS1770DEMO_UPDATE /* Check if all blocks are below ABSOLUTE_THRESHOLD */ zero_blocks_flag = (ZERO_BLOCKS == gated_loudness(gating_block_energy, 1.0, n_gating_blocks, ABSOLUTE_THRESHOLD, rms_flag) ); -#endif -#ifdef BS1770DEMO_UPDATE if ( !zero_input_flag && !zero_blocks_flag ) -#else - if( !zero_input_flag ) -#endif { if( f_output != NULL ) @@ -655,11 +607,7 @@ int main(int argc, char **argv ) /* Find scaling factor */ /* Since a rescaling affects the relative gating threshold the factor is found through an iterative function */ -#ifdef BS1770DEMO_UPDATE fac = find_scaling_factor( gating_block_energy, n_gating_blocks, lev_target, rms_flag, &lev_input, &lev_obtained ); -#else - fac = find_scaling_factor( gating_block_energy, n_gating_blocks, lev_target, &lev_input, &lev_obtained ); -#endif /* Apply scaling */ rewind( f_input ); @@ -678,9 +626,7 @@ int main(int argc, char **argv ) fprintf( stdout, "Target level: %.6f\n", lev_target ); fprintf( stdout, "Obtained level: %.6f\n", lev_obtained ); fprintf( stdout, "Scaling factor: %.6f\n", fac ); -#ifdef BS1770DEMO_UPDATE fprintf( stdout, "Scaling [dB]: %.6f\n", 20 * log10( fac ) ); -#endif fprintf( stdout, "\n--> Done processing %ld samples\n", length_total ); if( clip > 0 ) { @@ -692,18 +638,13 @@ int main(int argc, char **argv ) else { /* No output file is specified -- find the input level */ -#ifdef BS1770DEMO_UPDATE lev_input = gated_loudness_adaptive( gating_block_energy, 1.0, n_gating_blocks, rms_flag ); -#else - lev_input = gated_loudness_adaptive( gating_block_energy, 1.0, n_gating_blocks ); -#endif fprintf( stdout, "Input level: %.6f\n", lev_input ); fprintf( stdout, "\n--> Done processing %ld samples\n", length_total ); } } else { -#ifdef BS1770DEMO_UPDATE if ( zero_input_flag ) { fprintf(stderr, "*** Warning: All non-LFE channels are zero\n"); @@ -712,9 +653,6 @@ int main(int argc, char **argv ) { fprintf(stderr, "*** Warning: All non-LFE channels are below absolute gating threshold %.2f\n", ABSOLUTE_THRESHOLD); } -#else - fprintf( stderr, "*** Warning: All non-LFE channels are zero\n" ); -#endif if( f_output != NULL ) { fprintf( stderr, "*** Scaling of zero input not possible, exiting ..\n" ); From 4870d53b9976e936df3841a0b3263d3233fd59f8 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Sep 2023 13:37:32 +0200 Subject: [PATCH 08/53] remove push_indice(), set_indice(), get_indice(), exist_indice(), reset_indices(), write_indices(), read_indices(), read_bitstream_info(), reset_stack() and push_stack() from the list of reserved function names --- src/wmc_tool/c_parser.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index e2049422..b775d4b2 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -141,10 +141,7 @@ "printf fprintf " \ "fopen fclose fwrite fread " \ "exit " \ - "assert " \ - "push_indice set_indice get_indice exist_indice " \ - "reset_indices write_indices read_indices " \ - "read_bitstream_info reset_stack push_stack " + "assert " #define MATH_FUNCTS_STRING \ "abs fabs labs " \ From 28f198af736f92af8b9b3f0b5a1cc3907eee1015 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Sep 2023 13:38:52 +0200 Subject: [PATCH 09/53] fix the arguments of the itos() function --- src/wmc_tool/text_utils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wmc_tool/text_utils.cpp b/src/wmc_tool/text_utils.cpp index f2738c64..5d2cdfcb 100644 --- a/src/wmc_tool/text_utils.cpp +++ b/src/wmc_tool/text_utils.cpp @@ -454,10 +454,11 @@ char *mempwordicmp( const char *mem, const char *words ) /*-------------------------------------------------------------------* * itos (Integer to String) *-------------------------------------------------------------------*/ -char *itos( char *str, int value, int digits ) +char *itos( char *str, int value ) { size_t i, n; char *str2; + int digits = 3; /* Convert Value to String */ sprintf(str, "%d", value); From 46161a55892dc7c1b943ce2e0fdb631f64ad6a70 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Sep 2023 13:39:21 +0200 Subject: [PATCH 10/53] fix the declaration of the itos() function --- src/wmc_tool/text_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wmc_tool/text_utils.h b/src/wmc_tool/text_utils.h index b15a1b66..3b996859 100644 --- a/src/wmc_tool/text_utils.h +++ b/src/wmc_tool/text_utils.h @@ -109,7 +109,7 @@ char *memwordicmp( const char *mem, const char *words ); char *strwordistr( const char *mem, const char *words ); char *mempwordicmp( const char *mem, const char *words ); -char *itos( char *str, int value, int digits = 3 ); +char *itos( char *str, int value ); char *Fix_FileName( char *ptr ); From e60b8e223c02b617bcbd54092697920611fe40f8 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Sep 2023 13:41:25 +0200 Subject: [PATCH 11/53] - fix NaN printed in the complexity printout for a non-instrumented function - update of memory consumption counters is part of update_wmops() - replace the code updating the worst-case intra-frame and inter-frame heap memory consumption - support for per-frame and per-block memory consumption exported to a .csv file - fix incorrect calculation of the worst-case heap size for memory blocks allocated and de-allocated multiple times in a single frame --- src/wmc_tool/wmc_auto_c.txt | 190 +++++++++++++++++++----------------- src/wmc_tool/wmc_auto_h.txt | 4 - 2 files changed, 102 insertions(+), 92 deletions(-) diff --git a/src/wmc_tool/wmc_auto_c.txt b/src/wmc_tool/wmc_auto_c.txt index ec1726bb..58f024fe 100644 --- a/src/wmc_tool/wmc_auto_c.txt +++ b/src/wmc_tool/wmc_auto_c.txt @@ -340,12 +340,6 @@ "\r\n" " start_cnt = ops_cnt;\r\n" "\r\n" -" if ( heap_allocation_call_tree_size > 0 )\r\n" -" {\r\n" -" /* update intra-frame heap memory and inter-frame heap memory*/\r\n" -" update_mem();\r\n" -" }\r\n" -"\r\n" " /* increment frame counter */\r\n" " update_cnt++;\r\n" "\r\n" @@ -386,7 +380,7 @@ " }\r\n" "\r\n" " fprintf( stdout, sfmts, \"---------------\", \"------\", \"------\", \"------\", \"------\" );\r\n" -" fprintf( stdout, dfmts, \"total\", (float) update_cnt, FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt );\r\n" +" fprintf( stdout, dfmts, \"total\", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt );\r\n" " fprintf( stdout, \"\\n\" );\r\n" "\r\n" "#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" @@ -553,6 +547,11 @@ "#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) )\r\n" "#define IS_CALLOC( str ) ( str[0] == 'c' )\r\n" "\r\n" +"#ifdef MEM_COUNT_DETAILS\r\n" +"const char *csv_filename = \"mem_analysis.csv\";\r\n" +"static FILE *fid_csv_filename = NULL;\r\n" +"#endif\r\n" +"\r\n" "typedef struct\r\n" "{\r\n" " char function_name[MAX_FUNCTION_NAME_LENGTH + 1];\r\n" @@ -687,6 +686,25 @@ " size_wc_inter_frame_heap = 0;\r\n" " location_wc_inter_frame_heap = -1;\r\n" "\r\n" +"#ifdef MEM_COUNT_DETAILS\r\n" +" /* Check, if the .csv file has already been opened */\r\n" +" if ( fid_csv_filename == NULL )\r\n" +" {\r\n" +" fid_csv_filename = fopen( csv_filename, \"wb\" );\r\n" +"\r\n" +" if ( fid_csv_filename == NULL )\r\n" +" {\r\n" +" fprintf( stderr, \"\\nCannot open %s!\\n\\n\", csv_filename );\r\n" +" exit( -1 );\r\n" +" }\r\n" +" }\r\n" +" else\r\n" +" {\r\n" +" /* reset file */\r\n" +" rewind( fid_csv_filename );\r\n" +" }\r\n" +"#endif\r\n" +"\r\n" " return;\r\n" "}\r\n" "\r\n" @@ -959,6 +977,11 @@ " ptr_record->block_size = size;\r\n" " ptr_record->total_block_size += size;\r\n" "\r\n" +"#ifdef MEM_COUNT_DETAILS\r\n" +" /* Export heap memory allocation record to the .csv file */\r\n" +" fprintf( fid_csv_filename, \"A,%d,%s,%d,%d\\n\", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size );\r\n" +"#endif\r\n" +"\r\n" " if ( ptr_record->frame_allocated != -1 )\r\n" " {\r\n" " fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Error: Attempt to Allocate the Same Memory Block with Freeing it First!\" );\r\n" @@ -1294,6 +1317,11 @@ " /* Check, if Out-Of-Bounds Access has been Detected */\r\n" " ptr_record->OOB_Flag = mem_check_OOB( ptr_record );\r\n" "\r\n" +"#ifdef MEM_COUNT_DETAILS\r\n" +" /* Export heap memory de-allocation record to the .csv file */\r\n" +" fprintf( fid_csv_filename, \"D,%d,%s,%d,%d\\n\", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size );\r\n" +"#endif\r\n" +"\r\n" " /* De-Allocate Memory Block */\r\n" " tmp_ptr = (char *) ptr;\r\n" " tmp_ptr -= BLOCK_ROUNDING;\r\n" @@ -1332,11 +1360,11 @@ "void update_mem( void )\r\n" "{\r\n" " int i, j, flag_alloc = -1, i_record;\r\n" -" int32_t size_current_intra_frame_heap;\r\n" +" int size_current_intra_frame_heap;\r\n" " int *list_current_intra_frame_heap = NULL, n_items_current_intra_frame_heap;\r\n" " allocator_record *ptr_record;\r\n" "\r\n" -" /* process the heap allocation call tree */\r\n" +" /* process the heap allocation call tree and prepare lists of intra-frame and inter-frame heap memory blocks for this frame */\r\n" " n_items_current_intra_frame_heap = 0;\r\n" " size_current_intra_frame_heap = 0;\r\n" " for ( i = 0; i < heap_allocation_call_tree_size; i++ )\r\n" @@ -1364,7 +1392,7 @@ " memset( list_current_intra_frame_heap, -1, heap_allocation_call_tree_size * sizeof( int ) );\r\n" " }\r\n" "\r\n" -" /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */\r\n" +" /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */\r\n" " if ( i_record == 0 )\r\n" " {\r\n" " flag_alloc = 1;\r\n" @@ -1384,23 +1412,7 @@ " list_current_intra_frame_heap[n_items_current_intra_frame_heap++] = i_record;\r\n" " size_current_intra_frame_heap += ptr_record->block_size;\r\n" "\r\n" -" /* check, if this is the new worst-case */\r\n" -" if ( size_current_intra_frame_heap > size_wc_intra_frame_heap )\r\n" -" {\r\n" -" if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap )\r\n" -" {\r\n" -" /* resize list, if needed */\r\n" -" max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" /* save to wc list */\r\n" -" memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) );\r\n" -" n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap;\r\n" -" size_wc_intra_frame_heap = size_current_intra_frame_heap;\r\n" -" location_wc_intra_frame_heap = update_cnt;\r\n" -" ptr_record->wc_heap_size_intra_frame = ptr_record->block_size;\r\n" -" }\r\n" +" /* no need to re-size the list -> the initially allocated size should be large enough */\r\n" " }\r\n" " else\r\n" " {\r\n" @@ -1451,23 +1463,6 @@ "\r\n" " list_current_inter_frame_heap[n_items_current_inter_frame_heap++] = i_record;\r\n" " size_current_inter_frame_heap += ptr_record->block_size;\r\n" -"\r\n" -" /* check, if this is the new worst-case */\r\n" -" if ( size_current_inter_frame_heap > size_wc_inter_frame_heap )\r\n" -" {\r\n" -" if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap )\r\n" -" {\r\n" -" /* resize list, if needed */\r\n" -" max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) );\r\n" -" n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap;\r\n" -" size_wc_inter_frame_heap = size_current_inter_frame_heap;\r\n" -" location_wc_inter_frame_heap = update_cnt;\r\n" -" ptr_record->wc_heap_size_inter_frame = ptr_record->block_size;\r\n" -" }\r\n" " }\r\n" " else\r\n" " {\r\n" @@ -1490,9 +1485,60 @@ " }\r\n" " }\r\n" "\r\n" +" /* check, if this is the new worst-case for intra-frame heap memory */\r\n" +" if ( size_current_intra_frame_heap > size_wc_intra_frame_heap )\r\n" +" {\r\n" +" if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap )\r\n" +" {\r\n" +" /* resize the list, if needed */\r\n" +" max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n" +" list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) );\r\n" +" }\r\n" +"\r\n" +" /* copy current-frame list to worst-case list */\r\n" +" memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) );\r\n" +" n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap;\r\n" +" size_wc_intra_frame_heap = size_current_intra_frame_heap;\r\n" +" location_wc_intra_frame_heap = update_cnt;\r\n" +"\r\n" +" /* update the wc numbers in all individual records */\r\n" +" for ( i = 0; i < n_items_wc_intra_frame_heap; i++ )\r\n" +" {\r\n" +" i_record = list_wc_intra_frame_heap[i];\r\n" +" ptr_record = &( allocation_list[i_record] );\r\n" +" ptr_record->wc_heap_size_intra_frame = ptr_record->block_size;\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" /* check, if this is the new worst-case for inter-frame heap memory */\r\n" +" if ( size_current_inter_frame_heap > size_wc_inter_frame_heap )\r\n" +" {\r\n" +" if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap )\r\n" +" {\r\n" +" /* resize list, if needed */\r\n" +" max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n" +" list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) );\r\n" +" }\r\n" +"\r\n" +" /* copy current-frame list to worst-case list */\r\n" +" memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) );\r\n" +" n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap;\r\n" +" size_wc_inter_frame_heap = size_current_inter_frame_heap;\r\n" +" location_wc_inter_frame_heap = update_cnt;\r\n" +"\r\n" +" /* update the wc numbers in all individual records */\r\n" +" for ( i = 0; i < n_items_wc_inter_frame_heap; i++ )\r\n" +" {\r\n" +" i_record = list_wc_inter_frame_heap[i];\r\n" +" ptr_record = &( allocation_list[i_record] );\r\n" +" ptr_record->wc_heap_size_inter_frame = ptr_record->block_size;\r\n" +" }\r\n" +" }\r\n" +"\r\n" " /* reset heap allocation call tree */\r\n" " heap_allocation_call_tree_size = 0;\r\n" "\r\n" +" /* de-allocate list of intra-frame heap memory blocks in the current fraeme - it's needed only inside this function */\r\n" " if ( list_current_intra_frame_heap )\r\n" " {\r\n" " free( list_current_intra_frame_heap );\r\n" @@ -1607,7 +1653,7 @@ "\r\n" " if ( ptr_record->noccurances > 1 )\r\n" " {\r\n" -" sprintf( size_str, \"%dx%d %s\", ptr_record->noccurances, (int) ( ( ptr_record->noccurances * ptr_record->wc_heap_size_intra_frame ) >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n" +" sprintf( size_str, \"%dx%d %s\", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n" " }\r\n" " else\r\n" " {\r\n" @@ -1636,7 +1682,7 @@ " continue;\r\n" " }\r\n" " ptr_record = &( allocation_list[index_record] );\r\n" -" ptr_record->noccurances = 1; /* reset the counter because som blocks may be both, intra-frame and inter-frame */\r\n" +" ptr_record->noccurances = 1; /* reset the counter as some blocks may have been both, intra-frame and inter-frame */\r\n" " for ( j = i + 1; j < n_items_wc_inter_frame_heap; j++ )\r\n" " {\r\n" " index = list_wc_inter_frame_heap[j];\r\n" @@ -1655,7 +1701,7 @@ " }\r\n" "\r\n" " /* Print Header */\r\n" -" sprintf( buf, format_str, \"Function Name\", \"Line\", \"Type\", \"Function Parameters\", \"Maximum Size\", \"Usage\" );\r\n" +" sprintf( buf, format_str, \"Function Name\", \"Line\", \"Type\", \"Function Parameters\", \"Memory Size\", \"Usage\" );\r\n" " puts( buf );\r\n" " length = strlen( buf );\r\n" " sprintf( buf, \"%0*d\\n\", (int) length - 1, 0 );\r\n" @@ -1690,11 +1736,11 @@ " sprintf( line_str, \"%d\", ptr_record->lineno );\r\n" "\r\n" " /* prepare average usage & memory size strings */\r\n" -" sprintf( usage_str, \"%d%%\", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 1 ) ) * 100.0f ) );\r\n" +" sprintf( usage_str, \"%d%%\", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 0.1f ) ) * 100.0f + 0.5f ) );\r\n" "\r\n" " if ( ptr_record->noccurances > 1 )\r\n" " {\r\n" -" sprintf( size_str, \"%dx%d %s\", ptr_record->noccurances, (int) ( ( ptr_record->noccurances * ptr_record->wc_heap_size_inter_frame ) >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n" +" sprintf( size_str, \"%dx%d %s\", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n" " }\r\n" " else\r\n" " {\r\n" @@ -1712,45 +1758,6 @@ " return;\r\n" "}\r\n" "\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * export_mem()\r\n" -" *\r\n" -" * Export detailed (per-item) information about heap memory usage to a .csv file\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"void export_mem( const char *csv_filename )\r\n" -"{\r\n" -" int i;\r\n" -" static FILE *fid = NULL;\r\n" -" allocator_record *record_ptr;\r\n" -"\r\n" -" if ( csv_filename == NULL || strcmp( csv_filename, \"\" ) == 0 )\r\n" -" {\r\n" -" return;\r\n" -" }\r\n" -"\r\n" -" /* Check, if the .csv file has already been opened */\r\n" -" if ( fid == NULL )\r\n" -" {\r\n" -" fid = fopen( csv_filename, \"wb\" );\r\n" -"\r\n" -" if ( fid == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"\\nCannot open %s!\\n\\n\", csv_filename );\r\n" -" exit( -1 );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Export individual heap memory records to a .csv file */\r\n" -" for ( i = 0; i < Num_Records; i++ )\r\n" -" {\r\n" -" record_ptr = &( allocation_list[i] );\r\n" -" fprintf( fid, \"%s:%d,%d;\", record_ptr->name, record_ptr->lineno, record_ptr->block_size );\r\n" -" }\r\n" -" fprintf( fid, \"\\n\" );\r\n" -"\r\n" -" return;\r\n" -"}\r\n" "#endif\r\n" "\r\n" "/*-------------------------------------------------------------------*\r\n" @@ -1901,6 +1908,13 @@ " free( list_wc_inter_frame_heap );\r\n" " }\r\n" "\r\n" +"#ifdef MEM_COUNT_DETAILS\r\n" +" if ( fid_csv_filename != NULL )\r\n" +" {\r\n" +" fclose( fid_csv_filename );\r\n" +" }\r\n" +"#endif\r\n" +"\r\n" " return;\r\n" "}\r\n" "\r\n" diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index a5d54b3e..6a368067 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -1007,9 +1007,6 @@ "\r\n" "void reset_mem( Counting_Size cnt_size );\r\n" "void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] );\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -"void export_mem( const char *csv_filename );\r\n" -"#endif\r\n" "\r\n" "int push_stack( const char *filename, const char *fctname );\r\n" "int pop_stack( const char *filename, const char *fctname );\r\n" @@ -1031,7 +1028,6 @@ "#define free_( ptr ) free( ptr )\r\n" "#define reset_mem( cnt_size )\r\n" "#define print_mem( Const_Data_PROM_Table )\r\n" -"#define export_mem( csv_filename )\r\n" "\r\n" "#define push_stack( file, fct )\r\n" "#define pop_stack( file, fct )\r\n" From 6fff996b88464638139428fb30c7ac3252115225 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Sep 2023 14:27:58 +0200 Subject: [PATCH 12/53] update the reference files wmc_auto.h and wmc_auto.c used in ctest --- src/wmc_tool/test_data/ref/wmc_auto.c | 190 ++++++++++++++------------ src/wmc_tool/test_data/ref/wmc_auto.h | 4 - 2 files changed, 102 insertions(+), 92 deletions(-) diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index 69cf37a0..b86bc25f 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -340,12 +340,6 @@ void update_wmops( void ) start_cnt = ops_cnt; - if ( heap_allocation_call_tree_size > 0 ) - { - /* update intra-frame heap memory and inter-frame heap memory*/ - update_mem(); - } - /* increment frame counter */ update_cnt++; @@ -386,7 +380,7 @@ void print_wmops( void ) } fprintf( stdout, sfmts, "---------------", "------", "------", "------", "------" ); - fprintf( stdout, dfmts, "total", (float) update_cnt, FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt ); + fprintf( stdout, dfmts, "total", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt ); fprintf( stdout, "\n" ); #ifdef WMOPS_WC_FRAME_ANALYSIS @@ -553,6 +547,11 @@ void print_wmops( void ) #define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) ) #define IS_CALLOC( str ) ( str[0] == 'c' ) +#ifdef MEM_COUNT_DETAILS +const char *csv_filename = "mem_analysis.csv"; +static FILE *fid_csv_filename = NULL; +#endif + typedef struct { char function_name[MAX_FUNCTION_NAME_LENGTH + 1]; @@ -687,6 +686,25 @@ void reset_mem( Counting_Size cnt_size ) size_wc_inter_frame_heap = 0; location_wc_inter_frame_heap = -1; +#ifdef MEM_COUNT_DETAILS + /* Check, if the .csv file has already been opened */ + if ( fid_csv_filename == NULL ) + { + fid_csv_filename = fopen( csv_filename, "wb" ); + + if ( fid_csv_filename == NULL ) + { + fprintf( stderr, "\nCannot open %s!\n\n", csv_filename ); + exit( -1 ); + } + } + else + { + /* reset file */ + rewind( fid_csv_filename ); + } +#endif + return; } @@ -959,6 +977,11 @@ void *mem_alloc( ptr_record->block_size = size; ptr_record->total_block_size += size; +#ifdef MEM_COUNT_DETAILS + /* Export heap memory allocation record to the .csv file */ + fprintf( fid_csv_filename, "A,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); +#endif + if ( ptr_record->frame_allocated != -1 ) { fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Attempt to Allocate the Same Memory Block with Freeing it First!" ); @@ -1294,6 +1317,11 @@ void mem_free( const char *func_name, int func_lineno, void *ptr ) /* Check, if Out-Of-Bounds Access has been Detected */ ptr_record->OOB_Flag = mem_check_OOB( ptr_record ); +#ifdef MEM_COUNT_DETAILS + /* Export heap memory de-allocation record to the .csv file */ + fprintf( fid_csv_filename, "D,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); +#endif + /* De-Allocate Memory Block */ tmp_ptr = (char *) ptr; tmp_ptr -= BLOCK_ROUNDING; @@ -1332,11 +1360,11 @@ void mem_free( const char *func_name, int func_lineno, void *ptr ) void update_mem( void ) { int i, j, flag_alloc = -1, i_record; - int32_t size_current_intra_frame_heap; + int size_current_intra_frame_heap; int *list_current_intra_frame_heap = NULL, n_items_current_intra_frame_heap; allocator_record *ptr_record; - /* process the heap allocation call tree */ + /* process the heap allocation call tree and prepare lists of intra-frame and inter-frame heap memory blocks for this frame */ n_items_current_intra_frame_heap = 0; size_current_intra_frame_heap = 0; for ( i = 0; i < heap_allocation_call_tree_size; i++ ) @@ -1364,7 +1392,7 @@ void update_mem( void ) memset( list_current_intra_frame_heap, -1, heap_allocation_call_tree_size * sizeof( int ) ); } - /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ + /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ if ( i_record == 0 ) { flag_alloc = 1; @@ -1384,23 +1412,7 @@ void update_mem( void ) list_current_intra_frame_heap[n_items_current_intra_frame_heap++] = i_record; size_current_intra_frame_heap += ptr_record->block_size; - /* check, if this is the new worst-case */ - if ( size_current_intra_frame_heap > size_wc_intra_frame_heap ) - { - if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap ) - { - /* resize list, if needed */ - max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; - list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) ); - } - - /* save to wc list */ - memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) ); - n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap; - size_wc_intra_frame_heap = size_current_intra_frame_heap; - location_wc_intra_frame_heap = update_cnt; - ptr_record->wc_heap_size_intra_frame = ptr_record->block_size; - } + /* no need to re-size the list -> the initially allocated size should be large enough */ } else { @@ -1451,23 +1463,6 @@ void update_mem( void ) list_current_inter_frame_heap[n_items_current_inter_frame_heap++] = i_record; size_current_inter_frame_heap += ptr_record->block_size; - - /* check, if this is the new worst-case */ - if ( size_current_inter_frame_heap > size_wc_inter_frame_heap ) - { - if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap ) - { - /* resize list, if needed */ - max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; - list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) ); - } - - memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) ); - n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap; - size_wc_inter_frame_heap = size_current_inter_frame_heap; - location_wc_inter_frame_heap = update_cnt; - ptr_record->wc_heap_size_inter_frame = ptr_record->block_size; - } } else { @@ -1490,9 +1485,60 @@ void update_mem( void ) } } + /* check, if this is the new worst-case for intra-frame heap memory */ + if ( size_current_intra_frame_heap > size_wc_intra_frame_heap ) + { + if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap ) + { + /* resize the list, if needed */ + max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; + list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) ); + } + + /* copy current-frame list to worst-case list */ + memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) ); + n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap; + size_wc_intra_frame_heap = size_current_intra_frame_heap; + location_wc_intra_frame_heap = update_cnt; + + /* update the wc numbers in all individual records */ + for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) + { + i_record = list_wc_intra_frame_heap[i]; + ptr_record = &( allocation_list[i_record] ); + ptr_record->wc_heap_size_intra_frame = ptr_record->block_size; + } + } + + /* check, if this is the new worst-case for inter-frame heap memory */ + if ( size_current_inter_frame_heap > size_wc_inter_frame_heap ) + { + if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap ) + { + /* resize list, if needed */ + max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; + list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) ); + } + + /* copy current-frame list to worst-case list */ + memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) ); + n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap; + size_wc_inter_frame_heap = size_current_inter_frame_heap; + location_wc_inter_frame_heap = update_cnt; + + /* update the wc numbers in all individual records */ + for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) + { + i_record = list_wc_inter_frame_heap[i]; + ptr_record = &( allocation_list[i_record] ); + ptr_record->wc_heap_size_inter_frame = ptr_record->block_size; + } + } + /* reset heap allocation call tree */ heap_allocation_call_tree_size = 0; + /* de-allocate list of intra-frame heap memory blocks in the current fraeme - it's needed only inside this function */ if ( list_current_intra_frame_heap ) { free( list_current_intra_frame_heap ); @@ -1607,7 +1653,7 @@ static void mem_count_summary( void ) if ( ptr_record->noccurances > 1 ) { - sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ( ptr_record->noccurances * ptr_record->wc_heap_size_intra_frame ) >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); } else { @@ -1636,7 +1682,7 @@ static void mem_count_summary( void ) continue; } ptr_record = &( allocation_list[index_record] ); - ptr_record->noccurances = 1; /* reset the counter because som blocks may be both, intra-frame and inter-frame */ + ptr_record->noccurances = 1; /* reset the counter as some blocks may have been both, intra-frame and inter-frame */ for ( j = i + 1; j < n_items_wc_inter_frame_heap; j++ ) { index = list_wc_inter_frame_heap[j]; @@ -1655,7 +1701,7 @@ static void mem_count_summary( void ) } /* Print Header */ - sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Maximum Size", "Usage" ); + sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Memory Size", "Usage" ); puts( buf ); length = strlen( buf ); sprintf( buf, "%0*d\n", (int) length - 1, 0 ); @@ -1690,11 +1736,11 @@ static void mem_count_summary( void ) sprintf( line_str, "%d", ptr_record->lineno ); /* prepare average usage & memory size strings */ - sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 1 ) ) * 100.0f ) ); + sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 0.1f ) ) * 100.0f + 0.5f ) ); if ( ptr_record->noccurances > 1 ) { - sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ( ptr_record->noccurances * ptr_record->wc_heap_size_inter_frame ) >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); } else { @@ -1712,45 +1758,6 @@ static void mem_count_summary( void ) return; } -/*-------------------------------------------------------------------* - * export_mem() - * - * Export detailed (per-item) information about heap memory usage to a .csv file - *--------------------------------------------------------------------*/ - -void export_mem( const char *csv_filename ) -{ - int i; - static FILE *fid = NULL; - allocator_record *record_ptr; - - if ( csv_filename == NULL || strcmp( csv_filename, "" ) == 0 ) - { - return; - } - - /* Check, if the .csv file has already been opened */ - if ( fid == NULL ) - { - fid = fopen( csv_filename, "wb" ); - - if ( fid == NULL ) - { - fprintf( stderr, "\nCannot open %s!\n\n", csv_filename ); - exit( -1 ); - } - } - - /* Export individual heap memory records to a .csv file */ - for ( i = 0; i < Num_Records; i++ ) - { - record_ptr = &( allocation_list[i] ); - fprintf( fid, "%s:%d,%d;", record_ptr->name, record_ptr->lineno, record_ptr->block_size ); - } - fprintf( fid, "\n" ); - - return; -} #endif /*-------------------------------------------------------------------* @@ -1901,6 +1908,13 @@ void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) free( list_wc_inter_frame_heap ); } +#ifdef MEM_COUNT_DETAILS + if ( fid_csv_filename != NULL ) + { + fclose( fid_csv_filename ); + } +#endif + return; } diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index 08597ad6..989be871 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.h +++ b/src/wmc_tool/test_data/ref/wmc_auto.h @@ -1007,9 +1007,6 @@ void mem_free( const char *func_name, int func_lineno, void *ptr ); void reset_mem( Counting_Size cnt_size ); void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ); -#ifdef MEM_COUNT_DETAILS -void export_mem( const char *csv_filename ); -#endif int push_stack( const char *filename, const char *fctname ); int pop_stack( const char *filename, const char *fctname ); @@ -1031,7 +1028,6 @@ void reset_stack( void ); #define free_( ptr ) free( ptr ) #define reset_mem( cnt_size ) #define print_mem( Const_Data_PROM_Table ) -#define export_mem( csv_filename ) #define push_stack( file, fct ) #define pop_stack( file, fct ) From 791d011ca1517e3788a93ed5ea66f7d2fc6dd913 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Sep 2023 15:00:07 +0200 Subject: [PATCH 13/53] fix crash when no #include is present in the .c file --- src/wmc_tool/c_parser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index b775d4b2..cf26b420 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -8056,6 +8056,12 @@ TOOL_ERROR Include_Header( /* Find the End of the Last Contiguous Preprocessor Directive Block (#include) */ ptr_end = Find_End_Preproc_Block(NULL, ParseTbl_ptr); + /* if no #include is present in the file set the pointer to the beginning of the file */ + if (ptr_end == NULL) + { + ptr_end = ParseCtx_ptr->File.Data; + } + /* store the pointer for later use */ if (ptr_end_preproc_block != NULL) { From 58c72264f00dd57885d48b67685c385cc095d496 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Sep 2023 15:16:12 +0200 Subject: [PATCH 14/53] update version to 1.5 and year to 2023 --- src/wmc_tool/c_parser.cpp | 2 +- src/wmc_tool/c_parser.h | 2 +- src/wmc_tool/constants.h | 2 +- src/wmc_tool/output.cpp | 2 +- src/wmc_tool/output.h | 2 +- src/wmc_tool/parsing_defs.h | 2 +- src/wmc_tool/text_utils.cpp | 2 +- src/wmc_tool/text_utils.h | 2 +- src/wmc_tool/wmc_auto_c.txt | 2 +- src/wmc_tool/wmc_auto_h.txt | 2 +- src/wmc_tool/wmc_tool.cpp | 4 ++-- src/wmc_tool/wmc_tool.h | 4 ++-- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index cf26b420..f6bc28a9 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file diff --git a/src/wmc_tool/c_parser.h b/src/wmc_tool/c_parser.h index db15eaf5..fcdf8106 100644 --- a/src/wmc_tool/c_parser.h +++ b/src/wmc_tool/c_parser.h @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file diff --git a/src/wmc_tool/constants.h b/src/wmc_tool/constants.h index d75944bb..1dbd9138 100644 --- a/src/wmc_tool/constants.h +++ b/src/wmc_tool/constants.h @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file diff --git a/src/wmc_tool/output.cpp b/src/wmc_tool/output.cpp index 685b9058..ba43b4b6 100644 --- a/src/wmc_tool/output.cpp +++ b/src/wmc_tool/output.cpp @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file diff --git a/src/wmc_tool/output.h b/src/wmc_tool/output.h index 34ba4db9..015dfd07 100644 --- a/src/wmc_tool/output.h +++ b/src/wmc_tool/output.h @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file diff --git a/src/wmc_tool/parsing_defs.h b/src/wmc_tool/parsing_defs.h index d2863b26..ec2532d2 100644 --- a/src/wmc_tool/parsing_defs.h +++ b/src/wmc_tool/parsing_defs.h @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file diff --git a/src/wmc_tool/text_utils.cpp b/src/wmc_tool/text_utils.cpp index 5d2cdfcb..b7d55966 100644 --- a/src/wmc_tool/text_utils.cpp +++ b/src/wmc_tool/text_utils.cpp @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file diff --git a/src/wmc_tool/text_utils.h b/src/wmc_tool/text_utils.h index 3b996859..bfd1c443 100644 --- a/src/wmc_tool/text_utils.h +++ b/src/wmc_tool/text_utils.h @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file diff --git a/src/wmc_tool/wmc_auto_c.txt b/src/wmc_tool/wmc_auto_c.txt index 58f024fe..8851a0b1 100644 --- a/src/wmc_tool/wmc_auto_c.txt +++ b/src/wmc_tool/wmc_auto_c.txt @@ -1,5 +1,5 @@ "/*\r\n" -" * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved.\r\n" +" * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved.\r\n" " *\r\n" " * This software is protected by copyright law and by international treaties. The source code, and all of its derivations,\r\n" " * is provided by VoiceAge Corporation under the \"ITU-T Software Tools' General Public License\". Please, read the license file\r\n" diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index 6a368067..8284b88a 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -1,5 +1,5 @@ "/*\r\n" -" * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved.\r\n" +" * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved.\r\n" " *\r\n" " * This software is protected by copyright law and by international treaties. The source code, and all of its derivations,\r\n" " * is provided by VoiceAge Corporation under the \"ITU-T Software Tools' General Public License\". Please, read the license file\r\n" diff --git a/src/wmc_tool/wmc_tool.cpp b/src/wmc_tool/wmc_tool.cpp index fd102026..e9300e61 100644 --- a/src/wmc_tool/wmc_tool.cpp +++ b/src/wmc_tool/wmc_tool.cpp @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file @@ -1139,7 +1139,7 @@ int main( int argc, char *argv[] ) Print("\n" "WMC Tool (WMOPS Automatic Instrumentation Tool) v%s - %s\n" "\n" - "(C) 2022 copyright VoiceAge Corporation. All Rights Reserved.\n" + "(C) 2023 copyright VoiceAge Corporation. All Rights Reserved.\n" "\n" "This software is protected by copyright law and by international treaties. The source code, and all of its derivations,\n" "is provided by VoiceAge Corporation under the \"ITU-T Software Tools' General Public License\". Please, read the license file\n" diff --git a/src/wmc_tool/wmc_tool.h b/src/wmc_tool/wmc_tool.h index cda87822..95d961ef 100644 --- a/src/wmc_tool/wmc_tool.h +++ b/src/wmc_tool/wmc_tool.h @@ -1,5 +1,5 @@ /* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file @@ -19,7 +19,7 @@ * Switches *-------------------------------------------------------------------*/ -#define WMC_TOOL_VERSION_NO "1.4" /* Current version */ +#define WMC_TOOL_VERSION_NO "1.5" /* Current version */ /*#define DEBUG_PRINT*/ /* For debugging purposes */ /*-------------------------------------------------------------------* From a6c80ed55a55ca3c8042559c9d6c1179b21b35b3 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 14 Sep 2023 14:23:55 +0200 Subject: [PATCH 15/53] introduce -f fps command-line option for setting the FRAMES_PER_SECOND constant --- src/wmc_tool/test_data/ref/wmc_auto.c | 3853 +++++++++++++------------ src/wmc_tool/test_data/ref/wmc_auto.h | Bin 42791 -> 43832 bytes src/wmc_tool/wmc_tool.cpp | 131 +- 3 files changed, 2009 insertions(+), 1975 deletions(-) diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index b86bc25f..aefb6963 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -1,1926 +1,1927 @@ -/* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. - * - * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, - * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file - * or refer to ITU-T Recommendation G.191 on "SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS". - * - * Any use of this software is permitted provided that this notice is not removed and that neither the authors nor - * VoiceAge Corporation are deemed to have made any representations as to the suitability of this software - * for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE. - * - * Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca) - */ - -#include -#include -#include -#include -#include - -#ifndef _MSC_VER -#include -#include -#else -#include -#endif - -#include "wmc_auto.h" - - -#define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */ - -#ifdef WMOPS - -/*-------------------------------------------------------------------* - * Complexity counting tool - *--------------------------------------------------------------------*/ - -#define MAX_RECORDS 1024 -#define MAX_CHAR 64 -#define MAX_STACK 64 -#define DOUBLE_MAX 0x80000000 - -struct wmops_record -{ - char label[MAX_CHAR]; - long call_number; - long update_cnt; - int call_tree[MAX_RECORDS]; - double start_selfcnt; - double current_selfcnt; - double max_selfcnt; - double min_selfcnt; - double tot_selfcnt; - double start_cnt; /* The following take into account the decendants */ - double current_cnt; - double max_cnt; - double min_cnt; - double tot_cnt; -#ifdef WMOPS_WC_FRAME_ANALYSIS - int32_t current_call_number; - double wc_cnt; - double wc_selfcnt; - int32_t wc_call_number; -#endif -}; - -double ops_cnt; -double prom_cnt; -double inst_cnt[NUM_INST]; - -static struct wmops_record wmops[MAX_RECORDS]; -static int stack[MAX_STACK]; -static int sptr; -static int num_records; -static int current_record; -static long update_cnt; -static double start_cnt; -static double max_cnt; -static double min_cnt; -static double inst_cnt_wc[NUM_INST]; -static long fnum_cnt_wc; - -static int *heap_allocation_call_tree = NULL, heap_allocation_call_tree_size = 0, heap_allocation_call_tree_max_size = 0; - - -void reset_wmops( void ) -{ - int i, j; - - for ( i = 0; i < MAX_RECORDS; i++ ) - { - strcpy( &wmops[i].label[0], "\0" ); - wmops[i].call_number = 0; - wmops[i].update_cnt = 0; - for ( j = 0; j < MAX_RECORDS; j++ ) - { - wmops[i].call_tree[j] = -1; - } - wmops[i].start_selfcnt = 0.0; - wmops[i].current_selfcnt = 0.0; - wmops[i].max_selfcnt = 0.0; - wmops[i].min_selfcnt = DOUBLE_MAX; - wmops[i].tot_selfcnt = 0.0; - wmops[i].start_cnt = 0.0; - wmops[i].current_cnt = 0.0; - wmops[i].max_cnt = 0.0; - wmops[i].min_cnt = DOUBLE_MAX; - wmops[i].tot_cnt = 0.0; -#ifdef WMOPS_WC_FRAME_ANALYSIS - wmops[i].wc_cnt = 0.0; - wmops[i].wc_selfcnt = 0.0; - wmops[i].current_call_number = 0; -#endif - } - - for ( i = 0; i < MAX_STACK; i++ ) - { - stack[i] = -1; - } - sptr = 0; - num_records = 0; - current_record = -1; - update_cnt = 0; - - max_cnt = 0.0; - min_cnt = DOUBLE_MAX; - start_cnt = 0.0; - ops_cnt = 0.0; -} - - -void push_wmops( const char *label ) -{ - int new_flag; - int i, j; - - /* Check if new function record label */ - new_flag = 1; - for ( i = 0; i < num_records; i++ ) - { - if ( strcmp( wmops[i].label, label ) == 0 ) - { - new_flag = 0; - break; - } - } - - /* Configure new record */ - if ( new_flag ) - { - if ( num_records >= MAX_RECORDS ) - { - fprintf( stdout, "push_wmops(): exceeded MAX_RECORDS count.\n\n" ); - exit( -1 ); - } - strcpy( wmops[i].label, label ); - num_records++; - } - - /* Push current context onto stack */ - if ( current_record >= 0 ) - { - if ( sptr >= MAX_STACK ) - { - fprintf( stdout, "\r push_wmops(): stack exceeded, try inreasing MAX_STACK\n" ); - exit( -1 ); - } - stack[sptr++] = current_record; - - /* accumulate op counts */ - wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; - - /* update call tree */ - for ( j = 0; j < MAX_RECORDS; j++ ) - { - if ( wmops[i].call_tree[j] == current_record ) - { - break; - } - else if ( wmops[i].call_tree[j] == -1 ) - { - wmops[i].call_tree[j] = current_record; - break; - } - } - } - - /* init current record */ - current_record = i; - wmops[current_record].start_selfcnt = ops_cnt; - wmops[current_record].start_cnt = ops_cnt; - wmops[current_record].call_number++; -#ifdef WMOPS_WC_FRAME_ANALYSIS - wmops[current_record].current_call_number++; -#endif - - return; -} - - -void pop_wmops( void ) -{ - - /* Check for underflow */ - if ( current_record < 0 ) - { - fprintf( stdout, "\r pop_wmops(): stack underflow, too many calls to pop_wmops()\n" ); - exit( -1 ); - } - - /* update count of current record */ - wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; - wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt; - - /* Get back previous context from stack */ - if ( sptr > 0 ) - { - current_record = stack[--sptr]; - wmops[current_record].start_selfcnt = ops_cnt; - } - else - { - current_record = -1; - } - - return; -} - - -void update_wmops( void ) -{ - int i; - double current_cnt; -#ifdef WMOPS_PER_FRAME - static FILE *fid = NULL; - const char filename[] = "wmops_analysis"; - float tmpF; -#endif - - if ( sptr != 0 ) - { - fprintf( stdout, "update_wmops(): Stack must be empty!\n" ); - exit( -1 ); - } - -#ifdef WMOPS_PER_FRAME - /* Check, if the output file has already been opened */ - if ( fid == NULL ) - { - fid = fopen( filename, "wb" ); - - if ( fid == NULL ) - { - fprintf( stderr, "\nCannot open %s!\n\n", filename ); - exit( -1 ); - } - } - - /* Write current complexity to the external file */ - tmpF = (float) ( FAC * wmops[0].current_cnt ); - fwrite( &tmpF, sizeof( float ), 1, fid ); -#endif - -#ifdef WMOPS_WC_FRAME_ANALYSIS - if ( ops_cnt - start_cnt > max_cnt ) - { - for ( i = 0; i < num_records; i++ ) - { - wmops[i].wc_cnt = wmops[i].current_cnt; - wmops[i].wc_selfcnt = wmops[i].current_selfcnt; - wmops[i].wc_call_number = wmops[i].current_call_number; - } - } -#endif - - for ( i = 0; i < num_records; i++ ) - { - wmops[i].tot_selfcnt += wmops[i].current_selfcnt; - wmops[i].tot_cnt += wmops[i].current_cnt; - - if ( wmops[i].current_selfcnt > 0 ) - { - if ( wmops[i].current_selfcnt > wmops[i].max_selfcnt ) - { - wmops[i].max_selfcnt = wmops[i].current_selfcnt; - } - - if ( wmops[i].current_selfcnt < wmops[i].min_selfcnt ) - { - wmops[i].min_selfcnt = wmops[i].current_selfcnt; - } - } - - wmops[i].current_selfcnt = 0; - - if ( wmops[i].current_cnt > 0 ) - { - if ( wmops[i].current_cnt > wmops[i].max_cnt ) - { - wmops[i].max_cnt = wmops[i].current_cnt; - } - - if ( wmops[i].current_cnt < wmops[i].min_cnt ) - { - wmops[i].min_cnt = wmops[i].current_cnt; - } - - wmops[i].update_cnt++; - } - - wmops[i].current_cnt = 0; -#ifdef WMOPS_WC_FRAME_ANALYSIS - wmops[i].current_call_number = 0; -#endif - } - - current_cnt = ops_cnt - start_cnt; - if ( current_cnt > max_cnt ) - { - max_cnt = current_cnt; - - for ( i = 0; i < NUM_INST; i++ ) - { - inst_cnt_wc[i] = inst_cnt[i]; - } - - fnum_cnt_wc = update_cnt + 1; - } - - if ( current_cnt < min_cnt ) - { - min_cnt = current_cnt; - } - - for ( i = 0; i < NUM_INST; i++ ) - { - inst_cnt[i] = 0.0; - } - - start_cnt = ops_cnt; - - /* increment frame counter */ - update_cnt++; - - return; -} - - -void print_wmops( void ) -{ - int i; - - char *sfmts = "%20s %8s %8s %7s %7s\n"; - char *dfmts = "%20s %8.2f %8.3f %7.3f %7.3f\n"; - char *sfmt = "%20s %8s %8s %7s %7s %7s %7s %7s\n"; - char *dfmt = "%20s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\n"; - -#ifdef WMOPS_WC_FRAME_ANALYSIS - int j, label_len, max_label_len; - char *sfmtt = "%20s %4s %15s\n"; - char *dfmtt = "%20s %4d "; -#endif - - fprintf( stdout, "\n\n --- Complexity analysis [WMOPS] --- \n\n" ); - - fprintf( stdout, "%54s %23s\n", "|------ SELF ------|", "|--- CUMULATIVE ---|" ); - fprintf( stdout, sfmt, " routine", " calls", " min ", " max ", " avg ", " min ", " max ", " avg " ); - fprintf( stdout, sfmt, "---------------", "------", "------", "------", "------", "------", "------", "------" ); - - for ( i = 0; i < num_records; i++ ) - { - fprintf( stdout, dfmt, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt, - wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt, - FAC * wmops[i].max_selfcnt, - wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_selfcnt / wmops[i].update_cnt, - wmops[i].min_cnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_cnt, - FAC * wmops[i].max_cnt, - wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_cnt / wmops[i].update_cnt ); - } - - fprintf( stdout, sfmts, "---------------", "------", "------", "------", "------" ); - fprintf( stdout, dfmts, "total", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt ); - fprintf( stdout, "\n" ); - -#ifdef WMOPS_WC_FRAME_ANALYSIS - /* calculate maximum label length for compact prinout */ - max_label_len = 0; - for ( i = 0; i < num_records; i++ ) - { - label_len = strlen( wmops[i].label ); - if ( label_len > max_label_len ) - { - max_label_len = label_len; - } - } - max_label_len += 4; - - fprintf( stdout, "\nComplexity analysis for the worst-case frame %ld:\n", fnum_cnt_wc ); - fprintf( stdout, "%*s %8s %10s %12s\n", max_label_len, " routine", " calls", " SELF", " CUMULATIVE" ); - fprintf( stdout, "%*s %8s %10s %10s\n", max_label_len, "---------------", "------", "------", "----------" ); - - for ( i = 0; i < num_records; i++ ) - { - fprintf( stdout, "%*s %8d %10.3f %12.3f\n", max_label_len, wmops[i].label, wmops[i].wc_call_number, FAC * wmops[i].wc_selfcnt, FAC * wmops[i].wc_cnt ); - } - - fprintf( stdout, "\nCall Tree:\n\n" ); - fprintf( stdout, sfmtt, " function", "num", "called by: " ); - fprintf( stdout, sfmtt, "---------------", "---", "--------------" ); - - for ( i = 0; i < num_records; i++ ) - { - fprintf( stdout, dfmtt, wmops[i].label, i ); - for ( j = 0; wmops[i].call_tree[j] != -1; j++ ) - { - if ( j != 0 ) - { - fprintf( stdout, ", " ); - } - fprintf( stdout, "%d", wmops[i].call_tree[j] ); - } - fprintf( stdout, "\n" ); - } - - fprintf( stdout, sfmtt, "---------------", "---", "--------------" ); - fprintf( stdout, "\n\n" ); - - fprintf( stdout, "\nInstruction type analysis for the worst-case frame %ld:\n\n", fnum_cnt_wc ); /* added -- JPA */ - for ( i = 0; i < NUM_INST; i++ ) - { - switch ( (enum instructions) i ) - { - case _ADD: - fprintf( stdout, "\tAdds: %12.1f\n", inst_cnt_wc[i] ); - break; - case _ABS: - fprintf( stdout, "\tAbsolutes: %12.1f\n", inst_cnt_wc[i] ); - break; - case _MULT: - fprintf( stdout, "\tMultiplies: %12.1f\n", inst_cnt_wc[i] ); - break; - case _MAC: - fprintf( stdout, "\tMACs: %12.1f\n", inst_cnt_wc[i] ); - break; - case _MOVE: - fprintf( stdout, "\tMoves: %12.1f\n", inst_cnt_wc[i] ); - break; - case _STORE: - fprintf( stdout, "\tStores: %12.1f\n", inst_cnt_wc[i] ); - break; - case _LOGIC: - fprintf( stdout, "\tLogicals: %12.1f\n", inst_cnt_wc[i] ); - break; - case _SHIFT: - fprintf( stdout, "\tShifts: %12.1f\n", inst_cnt_wc[i] ); - break; - case _BRANCH: - fprintf( stdout, "\tBranches: %12.1f\n", inst_cnt_wc[i] ); - break; - case _DIV: - fprintf( stdout, "\tDivisions: %12.1f\n", inst_cnt_wc[i] ); - break; - case _SQRT: - fprintf( stdout, "\tSquare Root: %12.1f\n", inst_cnt_wc[i] ); - break; - case _TRANS: - fprintf( stdout, "\tTrans: %12.1f\n", inst_cnt_wc[i] ); - break; - case _FUNC: - fprintf( stdout, "\tFunc Call: %12.1f\n", inst_cnt_wc[i] ); - break; - case _LOOP: - fprintf( stdout, "\tLoop Init: %12.1f\n", inst_cnt_wc[i] ); - break; - case _INDIRECT: - fprintf( stdout, "\tIndirect Addr: %12.1f\n", inst_cnt_wc[i] ); - break; - case _PTR_INIT: - fprintf( stdout, "\tPointer Init: %12.1f\n", inst_cnt_wc[i] ); - break; - case _TEST: - fprintf( stdout, "\tExtra condit.: %12.1f\n", inst_cnt_wc[i] ); - break; - case _POWER: - fprintf( stdout, "\tExponential: %12.1f\n", inst_cnt_wc[i] ); - break; - case _LOG: - fprintf( stdout, "\tLogarithm: %12.1f\n", inst_cnt_wc[i] ); - break; - case _MISC: - fprintf( stdout, "\tAll other op.: %12.1f\n", inst_cnt_wc[i] ); - break; - default: - fprintf( stdout, "\tERROR: Invalid instruction type: %d\n\n", i ); - } - } -#endif - - return; -} - - -/*-------------------------------------------------------------------* - * Memory counting tool measuring RAM usage (stack and heap) - * - * Maximum RAM is measured by monitoring the total allocated memory (stack and heap) in each frame. - * - * Maximum stack is measured by monitoring the difference between the 'top' and 'bottom' of the stack. The 'bottom' of the stack is updated in each function - * with a macro 'func_start_' which is inserted automatically to all functions during the instrumentation process. - * - * Maximum heap is measured by summing the sizes of all memory blocks allocated by malloc() or calloc() and deallocated by free(). The maximum heap size is - * updated each time when the macros malloc_() or calloc_() is invoked. The macros 'malloc_ and calloc_' are inserted automatically during the instrumentation process. - * As part of heap measurements, intra-frame heap and inter-frame heap are measured separately. Intra-frame heap refers to heap memory which is allocated and deallocated - * within a single frame. Inter-frame heap, on the contrary, refers to heap memory which is reserved for more than one frame. - * - * In order to run the memory counting tool the function reset_mem(cnt_size) must be called at the beginning of the encoding/decoding process. - * The unit in which memory consumption is reported is set via the parameter 'cnt_size'. It can be set to 0 (bytes), 1 (32b words) or 2 (64b words). - * At the end of the encoding/decoding process, 'print_mem()' function may be called to print basic information about memory consumption. If the macro 'MEM_COUNT_DETAILS' - * is activated, detailed information is printed - * - * The macro 'WMOPS' needs to be activated to enable memory counting. To avoid the instrumentation of malloc()/calloc()/free() calls, use - * #define WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP macro pair around the malloc(), calloc() and free(). - *--------------------------------------------------------------------*/ - -#define MAX_RECORDABLE_CALLS 100 -#define MAX_FUNCTION_NAME_LENGTH 35 /* Maximum length that the function string will be truncated to */ -#define MAX_PARAMS_LENGTH 50 /* Maximum length that the parameter string will be truncated to */ -#define MAX_NUM_RECORDS 300 /* Initial maximum number of memory records -> mightb be increased during runtime, if needed */ -#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of memory records, increase the number of records by this number */ - -/* This is the value (in bytes) towards which the block size is rounded. For example, a block of 123 bytes, when using - a 32 bits system, will end up taking 124 bytes since the last unused byte cannot be used for another block. */ -#ifdef MEM_ALIGN_64BITS -#define BLOCK_ROUNDING 8 /* Align on 64 Bits */ -#else -#define BLOCK_ROUNDING 4 /* Align on 32 Bits */ -#endif - -#define N_32BITS_BLOCKS ( BLOCK_ROUNDING / sizeof( int32_t ) ) - -#define MAGIC_VALUE_OOB 0x12A534F0 /* Signature value which is inserted before and after each allocated memory block, used to detect out-of-bound access */ -#define MAGIC_VALUE_USED ( ~MAGIC_VALUE_OOB ) /* Value used to pre-fill allocated memory blocks, used to calculate actual memory usage */ -#define OOB_START 0x1 /* Flag indicating out-of-bounds access before memory block */ -#define OOB_END 0x2 /* Flag indicating out-of-bounds access after memory block */ - -#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) ) -#define IS_CALLOC( str ) ( str[0] == 'c' ) - -#ifdef MEM_COUNT_DETAILS -const char *csv_filename = "mem_analysis.csv"; -static FILE *fid_csv_filename = NULL; -#endif - -typedef struct -{ - char function_name[MAX_FUNCTION_NAME_LENGTH + 1]; - int16_t *stack_ptr; -} caller_info; - -caller_info stack_callers[2][MAX_RECORDABLE_CALLS]; - -typedef struct -{ - char name[MAX_FUNCTION_NAME_LENGTH + 1]; /* +1 for NUL */ - char params[1 + MAX_PARAMS_LENGTH + 1]; /* +1 for 'm'/'c' alloc & +1 for NUL */ - unsigned long hash; - int lineno; - void *block_ptr; - int block_size; - unsigned long total_block_size; /* Cumulative sum of the allocated size in the session */ - unsigned long total_used_size; /* Cumulative sum of the used size in the session */ - int wc_heap_size_intra_frame; /* Worst-Case Intra-Frame Heap Size */ - int wc_heap_size_inter_frame; /* Worst-Case Inter-Frame Heap Size */ - int frame_allocated; /* Frame number in which the Memory Block has been allocated (-1 if not allocated at the moment) */ - int OOB_Flag; - int noccurances; /* Number of times that the memory block has been allocated in a frame */ -} allocator_record; - -allocator_record *allocation_list = NULL; - -static int16_t *ptr_base_stack = 0; /* Pointer to the bottom of stack (base pointer). Stack grows up. */ -static int16_t *ptr_current_stack = 0; /* Pointer to the current stack pointer */ -static int16_t *ptr_max_stack = 0; /* Pointer to the maximum stack pointer (the farest point from the bottom of stack) */ -static int32_t wc_stack_frame = 0; /* Frame corresponding to the worst-case stack usage */ -static int32_t wc_ram_size, wc_ram_frame; -static int32_t current_heap_size; -static int current_calls = 0; -static char location_max_stack[256] = "undefined"; -static int Num_Records, Max_Num_Records; -static size_t Stat_Cnt_Size = USE_BYTES; -static const char *Count_Unit[] = { "bytes", "words", "words" }; - -static int *list_wc_intra_frame_heap, n_items_wc_intra_frame_heap, max_items_wc_intra_frame_heap, size_wc_intra_frame_heap, location_wc_intra_frame_heap; -static int *list_current_inter_frame_heap, n_items_current_inter_frame_heap, max_items_current_inter_frame_heap, size_current_inter_frame_heap; -static int *list_wc_inter_frame_heap, n_items_wc_inter_frame_heap, max_items_wc_inter_frame_heap, size_wc_inter_frame_heap, location_wc_inter_frame_heap; - -/* Local Functions */ -static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str ); -allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record ); -static void *mem_alloc_block( size_t size, const char *size_str ); - -/*-------------------------------------------------------------------* - * reset_mem() - * - * Initialize/reset memory counting tool (stack and heap) - *--------------------------------------------------------------------*/ - -void reset_mem( Counting_Size cnt_size ) -{ - int16_t something; - size_t tmp_size; - - /* initialize stack pointers */ - ptr_base_stack = &something; - ptr_max_stack = ptr_base_stack; - ptr_current_stack = ptr_base_stack; - - Stat_Cnt_Size = cnt_size; - - /* Check, if sizeof(int32_t) is 4 bytes */ - tmp_size = sizeof( int32_t ); - if ( tmp_size != 4 ) - { - fprintf( stderr, "Error: Expecting 'int32_t' to be a 32 Bits Integer!" ); - exit( -1 ); - } - - /* create allocation list for malloc() memory blocks */ - if ( allocation_list == NULL ) - { - allocation_list = malloc( MAX_NUM_RECORDS * sizeof( allocator_record ) ); - } - - if ( allocation_list == NULL ) - { - fprintf( stderr, "Error: Unable to Create List of Memory Blocks!" ); - exit( -1 ); - } - - Num_Records = 0; - Max_Num_Records = MAX_NUM_RECORDS; - - wc_ram_size = 0; - wc_ram_frame = -1; - current_heap_size = 0; - - /* heap allocation tree */ - heap_allocation_call_tree_max_size = MAX_NUM_RECORDS; - if ( heap_allocation_call_tree == NULL ) - { - heap_allocation_call_tree = (int *) malloc( heap_allocation_call_tree_max_size * sizeof( int ) ); - memset( heap_allocation_call_tree, -1, heap_allocation_call_tree_max_size * sizeof( int ) ); - } - heap_allocation_call_tree_size = 0; - - /* wc intra-frame heap */ - max_items_wc_intra_frame_heap = MAX_NUM_RECORDS; - if ( list_wc_intra_frame_heap == NULL ) - { - list_wc_intra_frame_heap = (int *) malloc( max_items_wc_intra_frame_heap * sizeof( int ) ); - memset( list_wc_intra_frame_heap, -1, max_items_wc_intra_frame_heap * sizeof( int ) ); - } - n_items_wc_intra_frame_heap = 0; - size_wc_intra_frame_heap = 0; - location_wc_intra_frame_heap = -1; - - /* current inter-frame heap */ - max_items_current_inter_frame_heap = MAX_NUM_RECORDS; - if ( list_current_inter_frame_heap == NULL ) - { - list_current_inter_frame_heap = (int *) malloc( max_items_current_inter_frame_heap * sizeof( int ) ); - memset( list_current_inter_frame_heap, -1, max_items_current_inter_frame_heap * sizeof( int ) ); - } - n_items_current_inter_frame_heap = 0; - size_current_inter_frame_heap = 0; - - /* wc inter-frame heap */ - max_items_wc_inter_frame_heap = MAX_NUM_RECORDS; - if ( list_wc_inter_frame_heap == NULL ) - { - list_wc_inter_frame_heap = (int *) malloc( max_items_wc_inter_frame_heap * sizeof( int ) ); - memset( list_wc_inter_frame_heap, -1, max_items_wc_inter_frame_heap * sizeof( int ) ); - } - n_items_wc_inter_frame_heap = 0; - size_wc_inter_frame_heap = 0; - location_wc_inter_frame_heap = -1; - -#ifdef MEM_COUNT_DETAILS - /* Check, if the .csv file has already been opened */ - if ( fid_csv_filename == NULL ) - { - fid_csv_filename = fopen( csv_filename, "wb" ); - - if ( fid_csv_filename == NULL ) - { - fprintf( stderr, "\nCannot open %s!\n\n", csv_filename ); - exit( -1 ); - } - } - else - { - /* reset file */ - rewind( fid_csv_filename ); - } -#endif - - return; -} - -/*-------------------------------------------------------------------* - * reset_stack() - * - * Reset stack pointer - *--------------------------------------------------------------------*/ - -void reset_stack( void ) -{ - int16_t something; - - /* initialize/reset stack pointers */ - ptr_base_stack = &something; - ptr_max_stack = ptr_base_stack; - ptr_current_stack = ptr_base_stack; - - return; -} - -/*-------------------------------------------------------------------* - * push_stack() - * - * Check the current stack pointer and update the maximum stack pointer, if new maximum found. - *--------------------------------------------------------------------*/ - -int push_stack( const char *filename, const char *fctname ) -{ - int16_t something; - int32_t current_stack_size; - - ptr_current_stack = &something; - - (void) *filename; /* to avoid compilation warning */ - - /* Is there room to save the caller's information? */ - if ( current_calls >= MAX_RECORDABLE_CALLS ) - { /* No */ - fprintf( stderr, "No more room to store call stack info. Please increase MAX_RECORDABLE_CALLS" ); - exit( -1 ); - } - - /* Valid Function Name? */ - if ( fctname[0] == 0 ) - { /* No */ - fprintf( stderr, "Invalid function name for call stack info." ); - exit( -1 ); - } - - /* Save the Name of the Calling Function in the Table */ - strncpy( stack_callers[0][current_calls].function_name, fctname, MAX_FUNCTION_NAME_LENGTH ); - stack_callers[0][current_calls].function_name[MAX_FUNCTION_NAME_LENGTH] = 0; /* Nul Terminate */ - - /* Save the Stack Pointer */ - stack_callers[0][current_calls].stack_ptr = ptr_current_stack; - - /* Increase Stack Calling Tree Level */ - current_calls++; - - /* Is this the First Time or the Worst Case? */ - if ( ptr_current_stack < ptr_max_stack || ptr_max_stack == NULL ) - { /* Yes */ - /* Save Info about it */ - ptr_max_stack = ptr_current_stack; - - wc_stack_frame = update_cnt; /* current frame number is stored in the variable update_cnt and updated in the function update_wmops() */ - strncpy( location_max_stack, fctname, sizeof( location_max_stack ) - 1 ); - location_max_stack[sizeof( location_max_stack ) - 1] = '\0'; - - /* Save Call Tree */ - memmove( stack_callers[1], stack_callers[0], sizeof( caller_info ) * current_calls ); - - /* Terminate the List (Unless Full) */ - if ( current_calls < MAX_RECORDABLE_CALLS ) - { - stack_callers[1][current_calls].function_name[0] = 0; - } - } - - /* Check, if This is the New Worst-Case RAM (stack + heap) */ - current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) ); - - if ( current_stack_size < 0 ) - { - /* prevent negative stack size */ - current_stack_size = 0; - } - - if ( current_stack_size + current_heap_size > wc_ram_size ) - { - wc_ram_size = current_stack_size + current_heap_size; - wc_ram_frame = update_cnt; - } - - return 0 /* for Now */; -} - -/*-------------------------------------------------------------------* - * pop_stack() - * - * Remove stack caller entry from the list - *--------------------------------------------------------------------*/ - -int pop_stack( const char *filename, const char *fctname ) -{ - caller_info *caller_info_ptr; - - (void) *filename; /* to avoid compilation warning */ - - /* Decrease Stack Calling */ - current_calls--; - - /* Get Pointer to Caller Information */ - caller_info_ptr = &stack_callers[0][current_calls]; - - /* Check, if Names Match */ - if ( strncmp( caller_info_ptr->function_name, fctname, MAX_FUNCTION_NAME_LENGTH ) != 0 ) - { - fprintf( stderr, "Invalid usage of pop_stack()" ); - exit( -1 ); - } - - /* Erase Entry */ - caller_info_ptr->function_name[0] = 0; - - /* Retrieve previous stack pointer */ - if ( current_calls == 0 ) - { - ptr_current_stack = ptr_base_stack; - } - else - { - ptr_current_stack = stack_callers[0][current_calls - 1].stack_ptr; - } - - return 0 /* for Now */; -} - -#ifdef MEM_COUNT_DETAILS -/*-------------------------------------------------------------------* - * print_stack_call_tree() - * - * Print detailed information about worst-case stack usage - *--------------------------------------------------------------------*/ - -static void print_stack_call_tree( void ) -{ - caller_info *caller_info_ptr; - int call_level; - char fctname[MAX_FUNCTION_NAME_LENGTH + 1]; - - fprintf( stdout, "\nList of functions when maximum stack size is reached:\n\n" ); - - caller_info_ptr = &stack_callers[1][0]; - for ( call_level = 0; call_level < MAX_RECORDABLE_CALLS; call_level++ ) - { - /* Done? */ - if ( caller_info_ptr->function_name[0] == 0 ) - { - break; - } - - /* Print Name */ - strncpy( fctname, caller_info_ptr->function_name, MAX_FUNCTION_NAME_LENGTH ); - strcat( fctname, "()" ); - fprintf( stdout, "%-42s", fctname ); - - /* Print Stack Usage (Based on Difference) */ - if ( call_level != 0 ) - { - fprintf( stdout, "%lu %s\n", ( ( ( caller_info_ptr - 1 )->stack_ptr - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); - } - else - { - fprintf( stdout, "%lu %s\n", ( ( ptr_base_stack - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); - } - - /* Advance */ - caller_info_ptr++; - } - - fprintf( stdout, "\n" ); - - return; -} -#endif - - -/*-------------------------------------------------------------------* - * mem_alloc() - * - * Creates new record, stores auxiliary information about which function allocated the memory, line number, parameters, etc. - * Finally, it allocates physical memory using malloc() - * The function also updates worst-case heap size and worst-case RAM size - *--------------------------------------------------------------------*/ - -void *mem_alloc( - const char *func_name, - int func_lineno, - size_t size, - char *size_str /* the first char indicates m-alloc or c-alloc */ ) -{ - int index_record; - int32_t current_stack_size; - unsigned long hash; - allocator_record *ptr_record; - - if ( size == 0 ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Size of Zero not Supported" ); - exit( -1 ); - } - - /* Search for an existing record (that has been de-allocated before) */ - index_record = 0; - while ( ( ptr_record = get_mem_record( &hash, func_name, func_lineno, size_str, &index_record ) ) != NULL ) - { - if ( ptr_record->frame_allocated == -1 ) - { - break; - } - else - { - index_record++; - } - } - - /* Create new record */ - if ( ptr_record == NULL ) - { - if ( Num_Records >= Max_Num_Records ) - { - /* There is no room for a new record -> reallocate memory */ - Max_Num_Records += MAX_NUM_RECORDS_REALLOC_STEP; - allocation_list = realloc( allocation_list, Max_Num_Records * sizeof( allocator_record ) ); - } - - ptr_record = &( allocation_list[Num_Records] ); - - /* Initialize new record */ - ptr_record->hash = hash; - ptr_record->noccurances = 0; - ptr_record->total_block_size = 0; - ptr_record->total_used_size = 0; - ptr_record->frame_allocated = -1; - ptr_record->OOB_Flag = 0; - ptr_record->wc_heap_size_intra_frame = -1; - ptr_record->wc_heap_size_inter_frame = -1; - - index_record = Num_Records; - Num_Records++; - } - - /* Allocate memory block for the new record, add signature before the beginning and after the memory block and fill it with magic value */ - ptr_record->block_ptr = mem_alloc_block( size, size_str ); - - if ( ptr_record->block_ptr == NULL ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Cannot Allocate Memory!" ); - exit( -1 ); - } - - /* Save all auxiliary information about the memory block */ - strncpy( ptr_record->name, func_name, MAX_FUNCTION_NAME_LENGTH ); - ptr_record->name[MAX_FUNCTION_NAME_LENGTH] = '\0'; - strncpy( ptr_record->params, size_str, MAX_PARAMS_LENGTH ); /* Note: The size string starts with either 'm' or 'c' to indicate 'm'alloc or 'c'alloc */ - ptr_record->params[MAX_PARAMS_LENGTH] = '\0'; - ptr_record->lineno = func_lineno; - ptr_record->block_size = size; - ptr_record->total_block_size += size; - -#ifdef MEM_COUNT_DETAILS - /* Export heap memory allocation record to the .csv file */ - fprintf( fid_csv_filename, "A,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); -#endif - - if ( ptr_record->frame_allocated != -1 ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Attempt to Allocate the Same Memory Block with Freeing it First!" ); - exit( -1 ); - } - - ptr_record->frame_allocated = update_cnt; /* Store the current frame number -> later it will be used to determine the total duration */ - - /* Update Heap Size in the current frame */ - current_heap_size += ptr_record->block_size; - - /* Check, if this is the new Worst-Case RAM (stack + heap) */ - current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) ); - if ( current_stack_size + current_heap_size > wc_ram_size ) - { - wc_ram_size = current_stack_size + current_heap_size; - wc_ram_frame = update_cnt; - } - - /* Add new entry to the heap allocation call tree */ - if ( heap_allocation_call_tree == NULL ) - { - fprintf( stderr, "Error: Heap allocation call tree not created!" ); - exit( -1 ); - } - - /* check, if the maximum size of the call tree has been reached -> resize if so */ - if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size ) - { - heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP; - heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) ); - } - - /* push new entry (positive number means push op, neagtive number means pop op; zero index must be converted to 0.01 :-) */ - heap_allocation_call_tree[heap_allocation_call_tree_size++] = index_record; - - return ptr_record->block_ptr; -} - -/*-------------------------------------------------------------------* - * mem_alloc_block() - * - * Physical allocation of memory using malloc(). Appends 'signature' before and after the block, - * pre-fills memory block with magic value - *--------------------------------------------------------------------*/ - -static void *mem_alloc_block( size_t size, const char *size_str ) -{ - size_t rounded_size; - void *block_ptr; - char *tmp_ptr; - size_t n, f; - int32_t fill_value; - int32_t *ptr32; - int32_t mask, temp; - - /* Round Up Block Size */ - rounded_size = ROUND_BLOCK_SIZE( size ); - - /* Allocate memory using the standard malloc() by adding room for Signature Values */ - block_ptr = malloc( rounded_size + BLOCK_ROUNDING * 2 ); - - if ( block_ptr == NULL ) - { - return NULL; - } - - /* Add Signature Before the Start of the Block */ - ptr32 = (int32_t *) block_ptr; - n = N_32BITS_BLOCKS; - do - { - *ptr32++ = MAGIC_VALUE_OOB; - } while ( --n ); - - /* Fill Memory Block with Magic Value or 0 */ - fill_value = MAGIC_VALUE_USED; - if ( IS_CALLOC( size_str ) ) - { - fill_value = 0x00000000; - } - n = size / sizeof( int32_t ); - while ( n-- ) - { - *ptr32++ = fill_value; - } - - /* Fill the Reminder of the Memory Block - After Rounding */ - n = rounded_size - size; - f = n % sizeof( int32_t ); - if ( f != 0 ) - { - /* when filling with '0' need to adapt the magic value */ - /* shift by [1->24, 2->16, 3->8] */ - mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); /* (1) */ - temp = MAGIC_VALUE_OOB & mask; - if ( fill_value != 0x0 ) - { /* for malloc merge fill value */ - temp += ( ~mask ) & MAGIC_VALUE_USED; - } /* for calloc the code in (1) above already introduces zeros */ - *ptr32++ = temp; - } - n /= sizeof( int32_t ); - n += N_32BITS_BLOCKS; - - /* Add Signature After the End of Block */ - do - { - *ptr32++ = MAGIC_VALUE_OOB; - } while ( --n ); - - /* Adjust the Memory Block Pointer (Magic Value Before and After the Memory Block Requested) */ - tmp_ptr = (char *) block_ptr; - tmp_ptr += BLOCK_ROUNDING; - block_ptr = (void *) tmp_ptr; - - return block_ptr; -} - -/*-------------------------------------------------------------------* - * mem_set_usage() - * - * Calculates actual usage of memory block by checking the magic value that was used to pre-fill - * each memory block during its allocation - *--------------------------------------------------------------------*/ - -static int mem_set_usage( allocator_record *record_ptr ) -{ - int total_bytes_used; - - size_t n; - int32_t *ptr32; - char *ptr8; - size_t total_bytes; - int32_t fill_value; - - fill_value = MAGIC_VALUE_USED; - if ( ( record_ptr->params[0] ) == 'c' ) - { - fill_value = 0x00000000; - } - - total_bytes = record_ptr->block_size; - - /* Check 4 bytes at a time */ - ptr32 = (int32_t *) record_ptr->block_ptr; - total_bytes_used = 0; - for ( n = total_bytes / sizeof( int32_t ); n > 0; n-- ) - { - if ( *ptr32++ != fill_value ) - { - total_bytes_used += sizeof( int32_t ); - } - } - - /* Check remaining bytes (If Applicable) 1 byte at a time */ - ptr8 = (char *) ptr32; - for ( n = total_bytes % sizeof( int32_t ); n > 0; n-- ) - { - if ( *ptr8++ != (char) fill_value ) - { - total_bytes_used++; - } - - /* Update Value */ - fill_value >>= 8; - } - - return total_bytes_used; -} - -/*-------------------------------------------------------------------* - * mem_check_OOB() - * - * Checks, if out-of-bounds access has occured. This is done by inspecting the 'signature' value - * taht has been added before and after the memory block during its allocation - *--------------------------------------------------------------------*/ - -static unsigned int mem_check_OOB( allocator_record *record_ptr ) -{ - int32_t *ptr32; - unsigned int OOB_Flag = 0x0; - int32_t mask; - size_t i; - int f; - - ptr32 = (int32_t *) record_ptr->block_ptr - N_32BITS_BLOCKS; - - /* Check the Signature at the Beginning of Memory Block */ - i = N_32BITS_BLOCKS; - do - { - if ( *ptr32++ ^ MAGIC_VALUE_OOB ) - { - OOB_Flag |= OOB_START; - } - } while ( --i ); - - /* Advance to End (Snap to lowest 32 Bits) */ - ptr32 += record_ptr->block_size / sizeof( int32_t ); - - /* Calculate Unused Space That has been added to get to the rounded Block Size */ - i = ROUND_BLOCK_SIZE( record_ptr->block_size ) - record_ptr->block_size; - - /* Partial Check of Signature at the End of Memory Block (for block size that has been rounded) */ - f = i % sizeof( int32_t ); - if ( f != 0 ) - { - mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); - if ( ( *ptr32++ ^ MAGIC_VALUE_OOB ) & mask ) - { - OOB_Flag |= OOB_END; - } - } - - /* Full Check of Signature at the End of Memory Block, i.e. all 32 Bits (for the remainder after rounding) */ - i /= sizeof( int32_t ); - i += N_32BITS_BLOCKS; - do - { - if ( *ptr32++ ^ MAGIC_VALUE_OOB ) - { - OOB_Flag |= OOB_END; - } - } while ( --i ); - - return OOB_Flag; -} - -/*-------------------------------------------------------------------* - * malloc_hash() - * - * Calculate hash from function name, line number and malloc size - *--------------------------------------------------------------------*/ - -static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str ) -{ - unsigned long hash = 5381; - const char *ptr_str; - - ptr_str = func_name; - while ( ptr_str != NULL && *ptr_str != '\0' ) - { - hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */ - } - - hash = ( ( hash << 5 ) + hash ) + func_lineno; /* hash * 33 + func_lineno */ - - ptr_str = size_str; - while ( ptr_str != NULL && *ptr_str != '\0' ) - { - hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */ - } - - return hash; -} - -/*-------------------------------------------------------------------* - * get_mem_record() - * - * Search for memory record in the internal list, return NULL if not found - * Start from index_record - *--------------------------------------------------------------------*/ - -allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record ) -{ - int i; - - if ( *index_record < 0 || *index_record > Num_Records ) - { - return NULL; - } - - /* calculate hash */ - *hash = malloc_hash( func_name, func_lineno, size_str ); - - for ( i = *index_record; i < Num_Records; i++ ) - { - /* check, if memory block is not allocated at the moment and the hash matches */ - if ( allocation_list[i].block_ptr == NULL && *hash == allocation_list[i].hash ) - { - *index_record = i; - return &( allocation_list[i] ); - } - } - - /* not found */ - *index_record = -1; - return NULL; -} - - -/*-------------------------------------------------------------------* - * mem_free() - * - * This function de-allocatesd the memory block and frees the mphysical memory with free(). - * It also updates actual and average usage of the memory block. - * - * Note: The record is not removed from the list and may be reused later on in mem_alloc()! - *--------------------------------------------------------------------*/ - -void mem_free( const char *func_name, int func_lineno, void *ptr ) -{ - int i, index_record; - char *tmp_ptr; - allocator_record *ptr_record; - - /* Search for the Block Pointer in the List */ - ptr_record = NULL; - index_record = -1; - for ( i = 0; i < Num_Records; i++ ) - { - if ( ptr == allocation_list[i].block_ptr ) - { /* Yes, Found it */ - ptr_record = &( allocation_list[i] ); - index_record = i; - break; - } - } - - if ( ptr_record == NULL ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Unable to Find Record Corresponding to the Allocated Memory Block!" ); - exit( -1 ); - } - - /* Update the Heap Size */ - current_heap_size -= ptr_record->block_size; - - /* Calculate the Actual Usage of the Memory Block (Look for Signature) */ - ptr_record->total_used_size += mem_set_usage( ptr_record ); - - /* Check, if Out-Of-Bounds Access has been Detected */ - ptr_record->OOB_Flag = mem_check_OOB( ptr_record ); - -#ifdef MEM_COUNT_DETAILS - /* Export heap memory de-allocation record to the .csv file */ - fprintf( fid_csv_filename, "D,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); -#endif - - /* De-Allocate Memory Block */ - tmp_ptr = (char *) ptr; - tmp_ptr -= BLOCK_ROUNDING; - ptr = (void *) tmp_ptr; - free( ptr ); - - /* Add new entry to the heap allocation call tree */ - if ( heap_allocation_call_tree == NULL ) - { - fprintf( stderr, "Error: Heap allocation call tree not created!" ); - exit( -1 ); - } - - /* check, if the maximum size of the call tree has been reached -> resize if so */ - if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size ) - { - heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP; - heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) ); - } - - heap_allocation_call_tree[heap_allocation_call_tree_size++] = -index_record; - - /* Reset memory block pointer (this is checked when updating wc intra-frame and inter-frame memory) */ - ptr_record->block_ptr = NULL; - - return; -} - - -/*-------------------------------------------------------------------* - * update_mem() - * - * This function updates the worst-case intra-frame memory and the worst-case inter-frame memory. - *--------------------------------------------------------------------*/ - -void update_mem( void ) -{ - int i, j, flag_alloc = -1, i_record; - int size_current_intra_frame_heap; - int *list_current_intra_frame_heap = NULL, n_items_current_intra_frame_heap; - allocator_record *ptr_record; - - /* process the heap allocation call tree and prepare lists of intra-frame and inter-frame heap memory blocks for this frame */ - n_items_current_intra_frame_heap = 0; - size_current_intra_frame_heap = 0; - for ( i = 0; i < heap_allocation_call_tree_size; i++ ) - { - /* get the record */ - i_record = heap_allocation_call_tree[i]; - - if ( i_record > 0 ) - { - flag_alloc = 1; - } - else if ( i_record < 0 ) - { - flag_alloc = 0; - i_record = -i_record; - } - ptr_record = &( allocation_list[i_record] ); - - if ( ptr_record->frame_allocated == update_cnt && ptr_record->block_ptr == NULL ) - { - /* intra-frame heap memory */ - if ( list_current_intra_frame_heap == NULL ) - { - list_current_intra_frame_heap = (int *) malloc( heap_allocation_call_tree_size * sizeof( int ) ); - memset( list_current_intra_frame_heap, -1, heap_allocation_call_tree_size * sizeof( int ) ); - } - - /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ - if ( i_record == 0 ) - { - flag_alloc = 1; - for ( j = 0; j < n_items_current_intra_frame_heap; j++ ) - { - if ( list_current_intra_frame_heap[j] == i_record ) - { - flag_alloc = 0; - break; - } - } - } - - if ( flag_alloc ) - { - /* add to list */ - list_current_intra_frame_heap[n_items_current_intra_frame_heap++] = i_record; - size_current_intra_frame_heap += ptr_record->block_size; - - /* no need to re-size the list -> the initially allocated size should be large enough */ - } - else - { - /* remove from list */ - for ( j = 0; j < n_items_current_intra_frame_heap; j++ ) - { - if ( list_current_intra_frame_heap[j] == i_record ) - { - break; - } - } - memmove( &list_current_intra_frame_heap[j], &list_current_intra_frame_heap[j + 1], ( n_items_current_intra_frame_heap - j ) * sizeof( int ) ); - n_items_current_intra_frame_heap--; - size_current_intra_frame_heap -= ptr_record->block_size; - - /* reset block size */ - ptr_record->frame_allocated = -1; - ptr_record->block_size = 0; - } - } - else - { - /* inter-frame heap memory */ - - /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ - if ( i_record == 0 ) - { - flag_alloc = 1; - for ( j = 0; j < n_items_current_inter_frame_heap; j++ ) - { - if ( list_current_inter_frame_heap[j] == i_record ) - { - flag_alloc = 0; - break; - } - } - } - - if ( flag_alloc ) - { - /* add to list */ - if ( n_items_current_inter_frame_heap >= max_items_current_inter_frame_heap ) - { - /* resize list, if needed */ - max_items_current_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; - list_current_inter_frame_heap = realloc( list_current_inter_frame_heap, max_items_current_inter_frame_heap * sizeof( int ) ); - } - - list_current_inter_frame_heap[n_items_current_inter_frame_heap++] = i_record; - size_current_inter_frame_heap += ptr_record->block_size; - } - else - { - /* remove from list */ - for ( j = 0; j < n_items_current_inter_frame_heap; j++ ) - { - if ( list_current_inter_frame_heap[j] == i_record ) - { - break; - } - } - memmove( &list_current_inter_frame_heap[j], &list_current_inter_frame_heap[j + 1], ( n_items_current_inter_frame_heap - j ) * sizeof( int ) ); - n_items_current_inter_frame_heap--; - size_current_inter_frame_heap -= ptr_record->block_size; - - /* reset block size */ - ptr_record->frame_allocated = -1; - ptr_record->block_size = 0; - } - } - } - - /* check, if this is the new worst-case for intra-frame heap memory */ - if ( size_current_intra_frame_heap > size_wc_intra_frame_heap ) - { - if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap ) - { - /* resize the list, if needed */ - max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; - list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) ); - } - - /* copy current-frame list to worst-case list */ - memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) ); - n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap; - size_wc_intra_frame_heap = size_current_intra_frame_heap; - location_wc_intra_frame_heap = update_cnt; - - /* update the wc numbers in all individual records */ - for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) - { - i_record = list_wc_intra_frame_heap[i]; - ptr_record = &( allocation_list[i_record] ); - ptr_record->wc_heap_size_intra_frame = ptr_record->block_size; - } - } - - /* check, if this is the new worst-case for inter-frame heap memory */ - if ( size_current_inter_frame_heap > size_wc_inter_frame_heap ) - { - if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap ) - { - /* resize list, if needed */ - max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; - list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) ); - } - - /* copy current-frame list to worst-case list */ - memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) ); - n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap; - size_wc_inter_frame_heap = size_current_inter_frame_heap; - location_wc_inter_frame_heap = update_cnt; - - /* update the wc numbers in all individual records */ - for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) - { - i_record = list_wc_inter_frame_heap[i]; - ptr_record = &( allocation_list[i_record] ); - ptr_record->wc_heap_size_inter_frame = ptr_record->block_size; - } - } - - /* reset heap allocation call tree */ - heap_allocation_call_tree_size = 0; - - /* de-allocate list of intra-frame heap memory blocks in the current fraeme - it's needed only inside this function */ - if ( list_current_intra_frame_heap ) - { - free( list_current_intra_frame_heap ); - } - - return; -} - -#ifdef MEM_COUNT_DETAILS -/*-------------------------------------------------------------------* - * subst() - * - * Substitute character in string - *--------------------------------------------------------------------*/ - -static void subst( char *s, char from, char to ) -{ - while ( *s == from ) - { - *s++ = to; - } - - return; -} - - -/*-------------------------------------------------------------------* - * mem_count_summary() - * - * Print detailed (per-item) information about heap memory usage - *--------------------------------------------------------------------*/ - -static void mem_count_summary( void ) -{ - int i, j, index, index_record; - size_t length; - char buf[300], format_str[50], name_str[MAX_FUNCTION_NAME_LENGTH + 3], parms_str[MAX_PARAMS_LENGTH + 1], type_str[10], usage_str[20], size_str[20], line_str[10]; - allocator_record *ptr_record, *ptr; - - /* Prepare format string */ - sprintf( format_str, "%%-%ds %%5s %%6s %%-%ds %%20s %%6s ", MAX_FUNCTION_NAME_LENGTH, MAX_PARAMS_LENGTH ); - - if ( n_items_wc_intra_frame_heap > 0 ) - { - /* Intra-Frame Heap Size */ - fprintf( stdout, "\nList of memory blocks when maximum intra-frame heap size is reached:\n\n" ); - - /* Find duplicate records (same hash and worst-case heap size) */ - for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) - { - index_record = list_wc_intra_frame_heap[i]; - if ( index_record == -1 ) - { - continue; - } - - ptr_record = &( allocation_list[index_record] ); - for ( j = i + 1; j < n_items_wc_intra_frame_heap; j++ ) - { - index = list_wc_intra_frame_heap[j]; - if ( index == -1 ) - { - continue; - } - ptr = &( allocation_list[index] ); - - if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_intra_frame == ptr_record->wc_heap_size_intra_frame ) - { - ptr_record->noccurances++; - list_wc_intra_frame_heap[j] = -1; - } - } - } - - /* Print Header */ - sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Maximum Size", "Usage" ); - puts( buf ); - length = strlen( buf ); - sprintf( buf, "%0*d\n", (int) length - 1, 0 ); - subst( buf, '0', '-' ); - puts( buf ); - - for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) - { - index_record = list_wc_intra_frame_heap[i]; - - if ( index_record != -1 ) - { - /* get the record */ - ptr_record = &( allocation_list[index_record] ); - - /* prepare information strings */ - strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH ); - strcat( name_str, "()" ); - name_str[MAX_FUNCTION_NAME_LENGTH] = '\0'; - strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH ); - parms_str[MAX_PARAMS_LENGTH] = '\0'; - - if ( ptr_record->params[0] == 'm' ) - { - strcpy( type_str, "malloc" ); - } - else - { - strcpy( type_str, "calloc" ); - } - - sprintf( line_str, "%d", ptr_record->lineno ); - - /* prepare average usage & memory size strings */ - sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 1 ) ) * 100.0f ) ); - - if ( ptr_record->noccurances > 1 ) - { - sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); - } - else - { - sprintf( size_str, "%d %s", (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); - } - - sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str ); - puts( buf ); - } - } - - fprintf( stdout, "\n" ); - } - - if ( n_items_wc_inter_frame_heap > 0 ) - { - /* Inter-Frame Heap Size */ - fprintf( stdout, "\nList of memory blocks when maximum inter-frame heap size is reached:\n\n" ); - - /* Find duplicate records (same hash and worst-case heap size) */ - for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) - { - index_record = list_wc_inter_frame_heap[i]; - if ( index_record == -1 ) - { - continue; - } - ptr_record = &( allocation_list[index_record] ); - ptr_record->noccurances = 1; /* reset the counter as some blocks may have been both, intra-frame and inter-frame */ - for ( j = i + 1; j < n_items_wc_inter_frame_heap; j++ ) - { - index = list_wc_inter_frame_heap[j]; - if ( index == -1 ) - { - continue; - } - ptr = &( allocation_list[index] ); - - if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_inter_frame == ptr_record->wc_heap_size_inter_frame ) - { - ptr_record->noccurances++; - list_wc_inter_frame_heap[j] = -1; - } - } - } - - /* Print Header */ - sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Memory Size", "Usage" ); - puts( buf ); - length = strlen( buf ); - sprintf( buf, "%0*d\n", (int) length - 1, 0 ); - subst( buf, '0', '-' ); - puts( buf ); - - for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) - { - index_record = list_wc_inter_frame_heap[i]; - - if ( index_record != -1 ) - { - /* get the record */ - ptr_record = &( allocation_list[index_record] ); - - /* prepare information strings */ - strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH ); - strcat( name_str, "()" ); - name_str[MAX_FUNCTION_NAME_LENGTH] = '\0'; - strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH ); - parms_str[MAX_PARAMS_LENGTH] = '\0'; - - if ( ptr_record->params[0] == 'm' ) - { - strcpy( type_str, "malloc" ); - } - else - { - strcpy( type_str, "calloc" ); - } - - sprintf( line_str, "%d", ptr_record->lineno ); - - /* prepare average usage & memory size strings */ - sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 0.1f ) ) * 100.0f + 0.5f ) ); - - if ( ptr_record->noccurances > 1 ) - { - sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); - } - else - { - sprintf( size_str, "%d %s", (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); - } - - sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str ); - puts( buf ); - } - } - - fprintf( stdout, "\n" ); - } - - return; -} - -#endif - -/*-------------------------------------------------------------------* - * print_mem() - * - * Print information about ROM and RAM memory usage - *--------------------------------------------------------------------*/ - -void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) -{ - int i, nElem; - - fprintf( stdout, "\n\n --- Memory usage --- \n\n" ); - - if ( Const_Data_PROM_Table != NULL ) - { - nElem = 0; - while ( strcmp( Const_Data_PROM_Table[nElem].file_spec, "" ) != 0 ) - nElem++; - - for ( i = 0; i < nElem; i++ ) - { - fprintf( stdout, "Program ROM size (%s): %d instruction words\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size ); - } - - for ( i = 0; i < nElem; i++ ) - { - if ( Const_Data_PROM_Table[i].Get_Const_Data_Size_Func == NULL ) - { - fprintf( stdout, "Error: Cannot retrieve or calculate Table ROM size of (%s)!\n", Const_Data_PROM_Table[i].file_spec ); - } - - fprintf( stdout, "Table ROM (const data) size (%s): %d %s\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].Get_Const_Data_Size_Func() >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); - } - } - else - { - fprintf( stdout, "Program ROM size: not available\n" ); - fprintf( stdout, "Table ROM (const data) size: not available\n" ); - } - - if ( wc_ram_size > 0 ) - { - fprintf( stdout, "Maximum RAM (stack + heap) size: %d %s in frame %d\n", wc_ram_size >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], wc_ram_frame ); - } - else - { - fprintf( stdout, "Maximum RAM (stack + heap) size: not available\n" ); - } - - /* check, if the stack is empty */ - if ( ptr_current_stack != ptr_base_stack ) - { - fprintf( stderr, "Warning: Stack is not empty.\n" ); - } - - if ( ptr_base_stack - ptr_max_stack > 0 ) - { - fprintf( stdout, "Maximum stack size: %lu %s in frame %d\n", ( ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], - wc_stack_frame ); - } - else - { - fprintf( stdout, "Maximum stack size: not available\n" ); - } - - /* last update of intra-frame memory and inter-frame memory, if needed */ - if ( heap_allocation_call_tree_size > 0 ) - { - update_mem(); - } - - /* check, if all memory blocks have been deallocated (freed) */ - for ( i = 0; i < Num_Records; i++ ) - { - if ( allocation_list[i].block_ptr != NULL ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", allocation_list[i].name, allocation_list[i].lineno, "Error: Memory Block has not been De-Allocated with free()!" ); - exit( -1 ); - } - } - - if ( n_items_wc_intra_frame_heap > 0 ) - { - fprintf( stdout, "Maximum intra-frame heap size: %d %s in frame %d\n", size_wc_intra_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_intra_frame_heap ); - } - else - { - fprintf( stdout, "Maximum intra-frame heap size: 0\n" ); - } - - if ( n_items_wc_inter_frame_heap > 0 ) - { - fprintf( stdout, "Maximum inter-frame heap size: %d %s in frame %d\n", size_wc_inter_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_inter_frame_heap ); - } - else - { - fprintf( stdout, "Maximum inter-frame heap size: 0\n" ); - } - -#ifdef MEM_COUNT_DETAILS - /* Print detailed information about worst-case stack usage */ - if ( ptr_base_stack - ptr_max_stack > 0 ) - { - print_stack_call_tree(); - } - - /* Print detailed information about worst-case heap usage */ - mem_count_summary(); -#endif - - if ( Stat_Cnt_Size > 0 ) - { - fprintf( stdout, "\nNote: 1 word = %d bits\n", 8 << Stat_Cnt_Size ); - fprintf( stdout, "This is an optimistic estimate of memory consumption assuming that each variable type is stored with sizeof(type) bits\n" ); - } - - if ( n_items_wc_intra_frame_heap > 0 ) - { - fprintf( stdout, "Intra-frame heap memory is allocated and de-allocated in the same frame\n" ); - } - - /* De-allocate list of heap memory blocks */ - if ( allocation_list != NULL ) - { - free( allocation_list ); - } - - /* De-allocate heap allocation call tree */ - if ( heap_allocation_call_tree != NULL ) - { - free( heap_allocation_call_tree ); - } - - /* De-allocate intra-frame and inter-frame heap lists */ - if ( list_wc_intra_frame_heap != NULL ) - { - free( list_wc_intra_frame_heap ); - } - - if ( list_current_inter_frame_heap != NULL ) - { - free( list_current_inter_frame_heap ); - } - - if ( list_wc_inter_frame_heap != NULL ) - { - free( list_wc_inter_frame_heap ); - } - -#ifdef MEM_COUNT_DETAILS - if ( fid_csv_filename != NULL ) - { - fclose( fid_csv_filename ); - } -#endif - - return; -} - -#endif /* WMOPS */ - -#ifndef WMOPS -int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */ -#endif - +/* + * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. + * + * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, + * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file + * or refer to ITU-T Recommendation G.191 on "SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS". + * + * Any use of this software is permitted provided that this notice is not removed and that neither the authors nor + * VoiceAge Corporation are deemed to have made any representations as to the suitability of this software + * for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE. + * + * Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca) + */ + +#include +#include +#include +#include +#include + +#ifndef _MSC_VER +#include +#include +#else +#include +#endif + +#include "wmc_auto.h" + + +#define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */ + +#ifdef WMOPS + +/*-------------------------------------------------------------------* + * Complexity counting tool + *--------------------------------------------------------------------*/ + +#define MAX_RECORDS 1024 +#define MAX_CHAR 64 +#define MAX_STACK 64 +#define DOUBLE_MAX 0x80000000 + +struct wmops_record +{ + char label[MAX_CHAR]; + long call_number; + long update_cnt; + int call_tree[MAX_RECORDS]; + double start_selfcnt; + double current_selfcnt; + double max_selfcnt; + double min_selfcnt; + double tot_selfcnt; + double start_cnt; /* The following take into account the decendants */ + double current_cnt; + double max_cnt; + double min_cnt; + double tot_cnt; +#ifdef WMOPS_WC_FRAME_ANALYSIS + int32_t current_call_number; + double wc_cnt; + double wc_selfcnt; + int32_t wc_call_number; +#endif +}; + +double ops_cnt; +double prom_cnt; +double inst_cnt[NUM_INST]; + +static struct wmops_record wmops[MAX_RECORDS]; +static int stack[MAX_STACK]; +static int sptr; +static int num_records; +static int current_record; +static long update_cnt; +static double start_cnt; +static double max_cnt; +static double min_cnt; +static double inst_cnt_wc[NUM_INST]; +static long fnum_cnt_wc; + +static int *heap_allocation_call_tree = NULL, heap_allocation_call_tree_size = 0, heap_allocation_call_tree_max_size = 0; + + +void reset_wmops( void ) +{ + int i, j; + + for ( i = 0; i < MAX_RECORDS; i++ ) + { + strcpy( &wmops[i].label[0], "\0" ); + wmops[i].call_number = 0; + wmops[i].update_cnt = 0; + for ( j = 0; j < MAX_RECORDS; j++ ) + { + wmops[i].call_tree[j] = -1; + } + wmops[i].start_selfcnt = 0.0; + wmops[i].current_selfcnt = 0.0; + wmops[i].max_selfcnt = 0.0; + wmops[i].min_selfcnt = DOUBLE_MAX; + wmops[i].tot_selfcnt = 0.0; + wmops[i].start_cnt = 0.0; + wmops[i].current_cnt = 0.0; + wmops[i].max_cnt = 0.0; + wmops[i].min_cnt = DOUBLE_MAX; + wmops[i].tot_cnt = 0.0; +#ifdef WMOPS_WC_FRAME_ANALYSIS + wmops[i].wc_cnt = 0.0; + wmops[i].wc_selfcnt = 0.0; + wmops[i].current_call_number = 0; +#endif + } + + for ( i = 0; i < MAX_STACK; i++ ) + { + stack[i] = -1; + } + sptr = 0; + num_records = 0; + current_record = -1; + update_cnt = 0; + + max_cnt = 0.0; + min_cnt = DOUBLE_MAX; + start_cnt = 0.0; + ops_cnt = 0.0; +} + + +void push_wmops( const char *label ) +{ + int new_flag; + int i, j; + + /* Check if new function record label */ + new_flag = 1; + for ( i = 0; i < num_records; i++ ) + { + if ( strcmp( wmops[i].label, label ) == 0 ) + { + new_flag = 0; + break; + } + } + + /* Configure new record */ + if ( new_flag ) + { + if ( num_records >= MAX_RECORDS ) + { + fprintf( stdout, "push_wmops(): exceeded MAX_RECORDS count.\n\n" ); + exit( -1 ); + } + strcpy( wmops[i].label, label ); + num_records++; + } + + /* Push current context onto stack */ + if ( current_record >= 0 ) + { + if ( sptr >= MAX_STACK ) + { + fprintf( stdout, "\r push_wmops(): stack exceeded, try inreasing MAX_STACK\n" ); + exit( -1 ); + } + stack[sptr++] = current_record; + + /* accumulate op counts */ + wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; + + /* update call tree */ + for ( j = 0; j < MAX_RECORDS; j++ ) + { + if ( wmops[i].call_tree[j] == current_record ) + { + break; + } + else if ( wmops[i].call_tree[j] == -1 ) + { + wmops[i].call_tree[j] = current_record; + break; + } + } + } + + /* init current record */ + current_record = i; + wmops[current_record].start_selfcnt = ops_cnt; + wmops[current_record].start_cnt = ops_cnt; + wmops[current_record].call_number++; +#ifdef WMOPS_WC_FRAME_ANALYSIS + wmops[current_record].current_call_number++; +#endif + + return; +} + + +void pop_wmops( void ) +{ + + /* Check for underflow */ + if ( current_record < 0 ) + { + fprintf( stdout, "\r pop_wmops(): stack underflow, too many calls to pop_wmops()\n" ); + exit( -1 ); + } + + /* update count of current record */ + wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; + wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt; + + /* Get back previous context from stack */ + if ( sptr > 0 ) + { + current_record = stack[--sptr]; + wmops[current_record].start_selfcnt = ops_cnt; + } + else + { + current_record = -1; + } + + return; +} + + +void update_wmops( void ) +{ + int i; + double current_cnt; +#ifdef WMOPS_PER_FRAME + static FILE *fid = NULL; + const char filename[] = "wmops_analysis"; + float tmpF; +#endif + + if ( sptr != 0 ) + { + fprintf( stdout, "update_wmops(): Stack must be empty!\n" ); + exit( -1 ); + } + +#ifdef WMOPS_PER_FRAME + /* Check, if the output file has already been opened */ + if ( fid == NULL ) + { + fid = fopen( filename, "wb" ); + + if ( fid == NULL ) + { + fprintf( stderr, "\nCannot open %s!\n\n", filename ); + exit( -1 ); + } + } + + /* Write current complexity to the external file */ + tmpF = (float) ( FAC * wmops[0].current_cnt ); + fwrite( &tmpF, sizeof( float ), 1, fid ); +#endif + +#ifdef WMOPS_WC_FRAME_ANALYSIS + if ( ops_cnt - start_cnt > max_cnt ) + { + for ( i = 0; i < num_records; i++ ) + { + wmops[i].wc_cnt = wmops[i].current_cnt; + wmops[i].wc_selfcnt = wmops[i].current_selfcnt; + wmops[i].wc_call_number = wmops[i].current_call_number; + } + } +#endif + + for ( i = 0; i < num_records; i++ ) + { + wmops[i].tot_selfcnt += wmops[i].current_selfcnt; + wmops[i].tot_cnt += wmops[i].current_cnt; + + if ( wmops[i].current_selfcnt > 0 ) + { + if ( wmops[i].current_selfcnt > wmops[i].max_selfcnt ) + { + wmops[i].max_selfcnt = wmops[i].current_selfcnt; + } + + if ( wmops[i].current_selfcnt < wmops[i].min_selfcnt ) + { + wmops[i].min_selfcnt = wmops[i].current_selfcnt; + } + } + + wmops[i].current_selfcnt = 0; + + if ( wmops[i].current_cnt > 0 ) + { + if ( wmops[i].current_cnt > wmops[i].max_cnt ) + { + wmops[i].max_cnt = wmops[i].current_cnt; + } + + if ( wmops[i].current_cnt < wmops[i].min_cnt ) + { + wmops[i].min_cnt = wmops[i].current_cnt; + } + + wmops[i].update_cnt++; + } + + wmops[i].current_cnt = 0; +#ifdef WMOPS_WC_FRAME_ANALYSIS + wmops[i].current_call_number = 0; +#endif + } + + current_cnt = ops_cnt - start_cnt; + if ( current_cnt > max_cnt ) + { + max_cnt = current_cnt; + + for ( i = 0; i < NUM_INST; i++ ) + { + inst_cnt_wc[i] = inst_cnt[i]; + } + + fnum_cnt_wc = update_cnt + 1; + } + + if ( current_cnt < min_cnt ) + { + min_cnt = current_cnt; + } + + for ( i = 0; i < NUM_INST; i++ ) + { + inst_cnt[i] = 0.0; + } + + start_cnt = ops_cnt; + + /* increment frame counter */ + update_cnt++; + + return; +} + + +void print_wmops( void ) +{ + int i; + + char *sfmts = "%20s %8s %8s %7s %7s\n"; + char *dfmts = "%20s %8.2f %8.3f %7.3f %7.3f\n"; + char *sfmt = "%20s %8s %8s %7s %7s %7s %7s %7s\n"; + char *dfmt = "%20s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\n"; + +#ifdef WMOPS_WC_FRAME_ANALYSIS + int j, label_len, max_label_len; + char *sfmtt = "%20s %4s %15s\n"; + char *dfmtt = "%20s %4d "; +#endif + + fprintf( stdout, "\n\n --- Complexity analysis [WMOPS] --- \n\n" ); + + fprintf( stdout, "%54s %23s\n", "|------ SELF ------|", "|--- CUMULATIVE ---|" ); + fprintf( stdout, sfmt, " routine", " calls", " min ", " max ", " avg ", " min ", " max ", " avg " ); + fprintf( stdout, sfmt, "---------------", "------", "------", "------", "------", "------", "------", "------" ); + + for ( i = 0; i < num_records; i++ ) + { + fprintf( stdout, dfmt, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt, + wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt, + FAC * wmops[i].max_selfcnt, + wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_selfcnt / wmops[i].update_cnt, + wmops[i].min_cnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_cnt, + FAC * wmops[i].max_cnt, + wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_cnt / wmops[i].update_cnt ); + } + + fprintf( stdout, sfmts, "---------------", "------", "------", "------", "------" ); + fprintf( stdout, dfmts, "total", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt ); + fprintf( stdout, "\n" ); + +#ifdef WMOPS_WC_FRAME_ANALYSIS + /* calculate maximum label length for compact prinout */ + max_label_len = 0; + for ( i = 0; i < num_records; i++ ) + { + label_len = strlen( wmops[i].label ); + if ( label_len > max_label_len ) + { + max_label_len = label_len; + } + } + max_label_len += 4; + + fprintf( stdout, "\nComplexity analysis for the worst-case frame %ld:\n", fnum_cnt_wc ); + fprintf( stdout, "%*s %8s %10s %12s\n", max_label_len, " routine", " calls", " SELF", " CUMULATIVE" ); + fprintf( stdout, "%*s %8s %10s %10s\n", max_label_len, "---------------", "------", "------", "----------" ); + + for ( i = 0; i < num_records; i++ ) + { + fprintf( stdout, "%*s %8d %10.3f %12.3f\n", max_label_len, wmops[i].label, wmops[i].wc_call_number, FAC * wmops[i].wc_selfcnt, FAC * wmops[i].wc_cnt ); + } + + fprintf( stdout, "\nCall Tree:\n\n" ); + fprintf( stdout, sfmtt, " function", "num", "called by: " ); + fprintf( stdout, sfmtt, "---------------", "---", "--------------" ); + + for ( i = 0; i < num_records; i++ ) + { + fprintf( stdout, dfmtt, wmops[i].label, i ); + for ( j = 0; wmops[i].call_tree[j] != -1; j++ ) + { + if ( j != 0 ) + { + fprintf( stdout, ", " ); + } + fprintf( stdout, "%d", wmops[i].call_tree[j] ); + } + fprintf( stdout, "\n" ); + } + + fprintf( stdout, sfmtt, "---------------", "---", "--------------" ); + fprintf( stdout, "\n\n" ); + + fprintf( stdout, "\nInstruction type analysis for the worst-case frame %ld:\n\n", fnum_cnt_wc ); /* added -- JPA */ + for ( i = 0; i < NUM_INST; i++ ) + { + switch ( (enum instructions) i ) + { + case _ADD: + fprintf( stdout, "\tAdds: %12.1f\n", inst_cnt_wc[i] ); + break; + case _ABS: + fprintf( stdout, "\tAbsolutes: %12.1f\n", inst_cnt_wc[i] ); + break; + case _MULT: + fprintf( stdout, "\tMultiplies: %12.1f\n", inst_cnt_wc[i] ); + break; + case _MAC: + fprintf( stdout, "\tMACs: %12.1f\n", inst_cnt_wc[i] ); + break; + case _MOVE: + fprintf( stdout, "\tMoves: %12.1f\n", inst_cnt_wc[i] ); + break; + case _STORE: + fprintf( stdout, "\tStores: %12.1f\n", inst_cnt_wc[i] ); + break; + case _LOGIC: + fprintf( stdout, "\tLogicals: %12.1f\n", inst_cnt_wc[i] ); + break; + case _SHIFT: + fprintf( stdout, "\tShifts: %12.1f\n", inst_cnt_wc[i] ); + break; + case _BRANCH: + fprintf( stdout, "\tBranches: %12.1f\n", inst_cnt_wc[i] ); + break; + case _DIV: + fprintf( stdout, "\tDivisions: %12.1f\n", inst_cnt_wc[i] ); + break; + case _SQRT: + fprintf( stdout, "\tSquare Root: %12.1f\n", inst_cnt_wc[i] ); + break; + case _TRANS: + fprintf( stdout, "\tTrans: %12.1f\n", inst_cnt_wc[i] ); + break; + case _FUNC: + fprintf( stdout, "\tFunc Call: %12.1f\n", inst_cnt_wc[i] ); + break; + case _LOOP: + fprintf( stdout, "\tLoop Init: %12.1f\n", inst_cnt_wc[i] ); + break; + case _INDIRECT: + fprintf( stdout, "\tIndirect Addr: %12.1f\n", inst_cnt_wc[i] ); + break; + case _PTR_INIT: + fprintf( stdout, "\tPointer Init: %12.1f\n", inst_cnt_wc[i] ); + break; + case _TEST: + fprintf( stdout, "\tExtra condit.: %12.1f\n", inst_cnt_wc[i] ); + break; + case _POWER: + fprintf( stdout, "\tExponential: %12.1f\n", inst_cnt_wc[i] ); + break; + case _LOG: + fprintf( stdout, "\tLogarithm: %12.1f\n", inst_cnt_wc[i] ); + break; + case _MISC: + fprintf( stdout, "\tAll other op.: %12.1f\n", inst_cnt_wc[i] ); + break; + default: + fprintf( stdout, "\tERROR: Invalid instruction type: %d\n\n", i ); + } + } +#endif + + return; +} + + +/*-------------------------------------------------------------------* + * Memory counting tool measuring RAM usage (stack and heap) + * + * Maximum RAM is measured by monitoring the total allocated memory (stack and heap) in each frame. + * + * Maximum stack is measured by monitoring the difference between the 'top' and 'bottom' of the stack. The 'bottom' of the stack is updated in each function + * with a macro 'func_start_' which is inserted automatically to all functions during the instrumentation process. + * + * Maximum heap is measured by summing the sizes of all memory blocks allocated by malloc() or calloc() and deallocated by free(). The maximum heap size is + * updated each time when the macros malloc_() or calloc_() is invoked. The macros 'malloc_ and calloc_' are inserted automatically during the instrumentation process. + * As part of heap measurements, intra-frame heap and inter-frame heap are measured separately. Intra-frame heap refers to heap memory which is allocated and deallocated + * within a single frame. Inter-frame heap, on the contrary, refers to heap memory which is reserved for more than one frame. + * + * In order to run the memory counting tool the function reset_mem(cnt_size) must be called at the beginning of the encoding/decoding process. + * The unit in which memory consumption is reported is set via the parameter 'cnt_size'. It can be set to 0 (bytes), 1 (32b words) or 2 (64b words). + * At the end of the encoding/decoding process, 'print_mem()' function may be called to print basic information about memory consumption. If the macro 'MEM_COUNT_DETAILS' + * is activated, detailed information is printed + * + * The macro 'WMOPS' needs to be activated to enable memory counting. To avoid the instrumentation of malloc()/calloc()/free() calls, use + * #define WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP macro pair around the malloc(), calloc() and free(). + *--------------------------------------------------------------------*/ + +#define MAX_RECORDABLE_CALLS 100 +#define MAX_FUNCTION_NAME_LENGTH 35 /* Maximum length that the function string will be truncated to */ +#define MAX_PARAMS_LENGTH 50 /* Maximum length that the parameter string will be truncated to */ +#define MAX_NUM_RECORDS 300 /* Initial maximum number of memory records -> mightb be increased during runtime, if needed */ +#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of memory records, increase the number of records by this number */ + +/* This is the value (in bytes) towards which the block size is rounded. For example, a block of 123 bytes, when using + a 32 bits system, will end up taking 124 bytes since the last unused byte cannot be used for another block. */ +#ifdef MEM_ALIGN_64BITS +#define BLOCK_ROUNDING 8 /* Align on 64 Bits */ +#else +#define BLOCK_ROUNDING 4 /* Align on 32 Bits */ +#endif + +#define N_32BITS_BLOCKS ( BLOCK_ROUNDING / sizeof( int32_t ) ) + +#define MAGIC_VALUE_OOB 0x12A534F0 /* Signature value which is inserted before and after each allocated memory block, used to detect out-of-bound access */ +#define MAGIC_VALUE_USED ( ~MAGIC_VALUE_OOB ) /* Value used to pre-fill allocated memory blocks, used to calculate actual memory usage */ +#define OOB_START 0x1 /* Flag indicating out-of-bounds access before memory block */ +#define OOB_END 0x2 /* Flag indicating out-of-bounds access after memory block */ + +#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) ) +#define IS_CALLOC( str ) ( str[0] == 'c' ) + +#ifdef MEM_COUNT_DETAILS +const char *csv_filename = "mem_analysis.csv"; +static FILE *fid_csv_filename = NULL; +#endif + +typedef struct +{ + char function_name[MAX_FUNCTION_NAME_LENGTH + 1]; + int16_t *stack_ptr; +} caller_info; + +caller_info stack_callers[2][MAX_RECORDABLE_CALLS]; + +typedef struct +{ + char name[MAX_FUNCTION_NAME_LENGTH + 1]; /* +1 for NUL */ + char params[1 + MAX_PARAMS_LENGTH + 1]; /* +1 for 'm'/'c' alloc & +1 for NUL */ + unsigned long hash; + int lineno; + void *block_ptr; + int block_size; + unsigned long total_block_size; /* Cumulative sum of the allocated size in the session */ + unsigned long total_used_size; /* Cumulative sum of the used size in the session */ + int wc_heap_size_intra_frame; /* Worst-Case Intra-Frame Heap Size */ + int wc_heap_size_inter_frame; /* Worst-Case Inter-Frame Heap Size */ + int frame_allocated; /* Frame number in which the Memory Block has been allocated (-1 if not allocated at the moment) */ + int OOB_Flag; + int noccurances; /* Number of times that the memory block has been allocated in a frame */ +} allocator_record; + +allocator_record *allocation_list = NULL; + +static int16_t *ptr_base_stack = 0; /* Pointer to the bottom of stack (base pointer). Stack grows up. */ +static int16_t *ptr_current_stack = 0; /* Pointer to the current stack pointer */ +static int16_t *ptr_max_stack = 0; /* Pointer to the maximum stack pointer (the farest point from the bottom of stack) */ +static int32_t wc_stack_frame = 0; /* Frame corresponding to the worst-case stack usage */ +static int32_t wc_ram_size, wc_ram_frame; +static int32_t current_heap_size; +static int current_calls = 0; +static char location_max_stack[256] = "undefined"; +static int Num_Records, Max_Num_Records; +static size_t Stat_Cnt_Size = USE_BYTES; +static const char *Count_Unit[] = { "bytes", "words", "words" }; + +static int *list_wc_intra_frame_heap, n_items_wc_intra_frame_heap, max_items_wc_intra_frame_heap, size_wc_intra_frame_heap, location_wc_intra_frame_heap; +static int *list_current_inter_frame_heap, n_items_current_inter_frame_heap, max_items_current_inter_frame_heap, size_current_inter_frame_heap; +static int *list_wc_inter_frame_heap, n_items_wc_inter_frame_heap, max_items_wc_inter_frame_heap, size_wc_inter_frame_heap, location_wc_inter_frame_heap; + +/* Local Functions */ +static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str ); +allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record ); +static void *mem_alloc_block( size_t size, const char *size_str ); + +/*-------------------------------------------------------------------* + * reset_mem() + * + * Initialize/reset memory counting tool (stack and heap) + *--------------------------------------------------------------------*/ + +void reset_mem( Counting_Size cnt_size ) +{ + int16_t something; + size_t tmp_size; + + /* initialize stack pointers */ + ptr_base_stack = &something; + ptr_max_stack = ptr_base_stack; + ptr_current_stack = ptr_base_stack; + + Stat_Cnt_Size = cnt_size; + + /* Check, if sizeof(int32_t) is 4 bytes */ + tmp_size = sizeof( int32_t ); + if ( tmp_size != 4 ) + { + fprintf( stderr, "Error: Expecting 'int32_t' to be a 32 Bits Integer!" ); + exit( -1 ); + } + + /* create allocation list for malloc() memory blocks */ + if ( allocation_list == NULL ) + { + allocation_list = malloc( MAX_NUM_RECORDS * sizeof( allocator_record ) ); + } + + if ( allocation_list == NULL ) + { + fprintf( stderr, "Error: Unable to Create List of Memory Blocks!" ); + exit( -1 ); + } + + Num_Records = 0; + Max_Num_Records = MAX_NUM_RECORDS; + + wc_ram_size = 0; + wc_ram_frame = -1; + current_heap_size = 0; + + /* heap allocation tree */ + heap_allocation_call_tree_max_size = MAX_NUM_RECORDS; + if ( heap_allocation_call_tree == NULL ) + { + heap_allocation_call_tree = (int *) malloc( heap_allocation_call_tree_max_size * sizeof( int ) ); + memset( heap_allocation_call_tree, -1, heap_allocation_call_tree_max_size * sizeof( int ) ); + } + heap_allocation_call_tree_size = 0; + + /* wc intra-frame heap */ + max_items_wc_intra_frame_heap = MAX_NUM_RECORDS; + if ( list_wc_intra_frame_heap == NULL ) + { + list_wc_intra_frame_heap = (int *) malloc( max_items_wc_intra_frame_heap * sizeof( int ) ); + memset( list_wc_intra_frame_heap, -1, max_items_wc_intra_frame_heap * sizeof( int ) ); + } + n_items_wc_intra_frame_heap = 0; + size_wc_intra_frame_heap = 0; + location_wc_intra_frame_heap = -1; + + /* current inter-frame heap */ + max_items_current_inter_frame_heap = MAX_NUM_RECORDS; + if ( list_current_inter_frame_heap == NULL ) + { + list_current_inter_frame_heap = (int *) malloc( max_items_current_inter_frame_heap * sizeof( int ) ); + memset( list_current_inter_frame_heap, -1, max_items_current_inter_frame_heap * sizeof( int ) ); + } + n_items_current_inter_frame_heap = 0; + size_current_inter_frame_heap = 0; + + /* wc inter-frame heap */ + max_items_wc_inter_frame_heap = MAX_NUM_RECORDS; + if ( list_wc_inter_frame_heap == NULL ) + { + list_wc_inter_frame_heap = (int *) malloc( max_items_wc_inter_frame_heap * sizeof( int ) ); + memset( list_wc_inter_frame_heap, -1, max_items_wc_inter_frame_heap * sizeof( int ) ); + } + n_items_wc_inter_frame_heap = 0; + size_wc_inter_frame_heap = 0; + location_wc_inter_frame_heap = -1; + +#ifdef MEM_COUNT_DETAILS + /* Check, if the .csv file has already been opened */ + if ( fid_csv_filename == NULL ) + { + fid_csv_filename = fopen( csv_filename, "wb" ); + + if ( fid_csv_filename == NULL ) + { + fprintf( stderr, "\nCannot open %s!\n\n", csv_filename ); + exit( -1 ); + } + } + else + { + /* reset file */ + rewind( fid_csv_filename ); + } +#endif + + return; +} + +/*-------------------------------------------------------------------* + * reset_stack() + * + * Reset stack pointer + *--------------------------------------------------------------------*/ + +void reset_stack( void ) +{ + int16_t something; + + /* initialize/reset stack pointers */ + ptr_base_stack = &something; + ptr_max_stack = ptr_base_stack; + ptr_current_stack = ptr_base_stack; + + return; +} + +/*-------------------------------------------------------------------* + * push_stack() + * + * Check the current stack pointer and update the maximum stack pointer, if new maximum found. + *--------------------------------------------------------------------*/ + +int push_stack( const char *filename, const char *fctname ) +{ + int16_t something; + int32_t current_stack_size; + + ptr_current_stack = &something; + + (void) *filename; /* to avoid compilation warning */ + + /* Is there room to save the caller's information? */ + if ( current_calls >= MAX_RECORDABLE_CALLS ) + { /* No */ + fprintf( stderr, "No more room to store call stack info. Please increase MAX_RECORDABLE_CALLS" ); + exit( -1 ); + } + + /* Valid Function Name? */ + if ( fctname[0] == 0 ) + { /* No */ + fprintf( stderr, "Invalid function name for call stack info." ); + exit( -1 ); + } + + /* Save the Name of the Calling Function in the Table */ + strncpy( stack_callers[0][current_calls].function_name, fctname, MAX_FUNCTION_NAME_LENGTH ); + stack_callers[0][current_calls].function_name[MAX_FUNCTION_NAME_LENGTH] = 0; /* Nul Terminate */ + + /* Save the Stack Pointer */ + stack_callers[0][current_calls].stack_ptr = ptr_current_stack; + + /* Increase Stack Calling Tree Level */ + current_calls++; + + /* Is this the First Time or the Worst Case? */ + if ( ptr_current_stack < ptr_max_stack || ptr_max_stack == NULL ) + { /* Yes */ + /* Save Info about it */ + ptr_max_stack = ptr_current_stack; + + wc_stack_frame = update_cnt; /* current frame number is stored in the variable update_cnt and updated in the function update_wmops() */ + strncpy( location_max_stack, fctname, sizeof( location_max_stack ) - 1 ); + location_max_stack[sizeof( location_max_stack ) - 1] = '\0'; + + /* Save Call Tree */ + memmove( stack_callers[1], stack_callers[0], sizeof( caller_info ) * current_calls ); + + /* Terminate the List (Unless Full) */ + if ( current_calls < MAX_RECORDABLE_CALLS ) + { + stack_callers[1][current_calls].function_name[0] = 0; + } + } + + /* Check, if This is the New Worst-Case RAM (stack + heap) */ + current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) ); + + if ( current_stack_size < 0 ) + { + /* prevent negative stack size */ + current_stack_size = 0; + } + + if ( current_stack_size + current_heap_size > wc_ram_size ) + { + wc_ram_size = current_stack_size + current_heap_size; + wc_ram_frame = update_cnt; + } + + return 0 /* for Now */; +} + +/*-------------------------------------------------------------------* + * pop_stack() + * + * Remove stack caller entry from the list + *--------------------------------------------------------------------*/ + +int pop_stack( const char *filename, const char *fctname ) +{ + caller_info *caller_info_ptr; + + (void) *filename; /* to avoid compilation warning */ + + /* Decrease Stack Calling */ + current_calls--; + + /* Get Pointer to Caller Information */ + caller_info_ptr = &stack_callers[0][current_calls]; + + /* Check, if Names Match */ + if ( strncmp( caller_info_ptr->function_name, fctname, MAX_FUNCTION_NAME_LENGTH ) != 0 ) + { + fprintf( stderr, "Invalid usage of pop_stack()" ); + exit( -1 ); + } + + /* Erase Entry */ + caller_info_ptr->function_name[0] = 0; + + /* Retrieve previous stack pointer */ + if ( current_calls == 0 ) + { + ptr_current_stack = ptr_base_stack; + } + else + { + ptr_current_stack = stack_callers[0][current_calls - 1].stack_ptr; + } + + return 0 /* for Now */; +} + +#ifdef MEM_COUNT_DETAILS +/*-------------------------------------------------------------------* + * print_stack_call_tree() + * + * Print detailed information about worst-case stack usage + *--------------------------------------------------------------------*/ + +static void print_stack_call_tree( void ) +{ + caller_info *caller_info_ptr; + int call_level; + char fctname[MAX_FUNCTION_NAME_LENGTH + 1]; + + fprintf( stdout, "\nList of functions when maximum stack size is reached:\n\n" ); + + caller_info_ptr = &stack_callers[1][0]; + for ( call_level = 0; call_level < MAX_RECORDABLE_CALLS; call_level++ ) + { + /* Done? */ + if ( caller_info_ptr->function_name[0] == 0 ) + { + break; + } + + /* Print Name */ + strncpy( fctname, caller_info_ptr->function_name, MAX_FUNCTION_NAME_LENGTH ); + strcat( fctname, "()" ); + fprintf( stdout, "%-42s", fctname ); + + /* Print Stack Usage (Based on Difference) */ + if ( call_level != 0 ) + { + fprintf( stdout, "%lu %s\n", ( ( ( caller_info_ptr - 1 )->stack_ptr - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); + } + else + { + fprintf( stdout, "%lu %s\n", ( ( ptr_base_stack - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); + } + + /* Advance */ + caller_info_ptr++; + } + + fprintf( stdout, "\n" ); + + return; +} +#endif + + +/*-------------------------------------------------------------------* + * mem_alloc() + * + * Creates new record, stores auxiliary information about which function allocated the memory, line number, parameters, etc. + * Finally, it allocates physical memory using malloc() + * The function also updates worst-case heap size and worst-case RAM size + *--------------------------------------------------------------------*/ + +void *mem_alloc( + const char *func_name, + int func_lineno, + size_t size, + char *size_str /* the first char indicates m-alloc or c-alloc */ ) +{ + int index_record; + int32_t current_stack_size; + unsigned long hash; + allocator_record *ptr_record; + + if ( size == 0 ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Size of Zero not Supported" ); + exit( -1 ); + } + + /* Search for an existing record (that has been de-allocated before) */ + index_record = 0; + while ( ( ptr_record = get_mem_record( &hash, func_name, func_lineno, size_str, &index_record ) ) != NULL ) + { + if ( ptr_record->frame_allocated == -1 ) + { + break; + } + else + { + index_record++; + } + } + + /* Create new record */ + if ( ptr_record == NULL ) + { + if ( Num_Records >= Max_Num_Records ) + { + /* There is no room for a new record -> reallocate memory */ + Max_Num_Records += MAX_NUM_RECORDS_REALLOC_STEP; + allocation_list = realloc( allocation_list, Max_Num_Records * sizeof( allocator_record ) ); + } + + ptr_record = &( allocation_list[Num_Records] ); + + /* Initialize new record */ + ptr_record->hash = hash; + ptr_record->noccurances = 0; + ptr_record->total_block_size = 0; + ptr_record->total_used_size = 0; + ptr_record->frame_allocated = -1; + ptr_record->OOB_Flag = 0; + ptr_record->wc_heap_size_intra_frame = -1; + ptr_record->wc_heap_size_inter_frame = -1; + + index_record = Num_Records; + Num_Records++; + } + + /* Allocate memory block for the new record, add signature before the beginning and after the memory block and fill it with magic value */ + ptr_record->block_ptr = mem_alloc_block( size, size_str ); + + if ( ptr_record->block_ptr == NULL ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Cannot Allocate Memory!" ); + exit( -1 ); + } + + /* Save all auxiliary information about the memory block */ + strncpy( ptr_record->name, func_name, MAX_FUNCTION_NAME_LENGTH ); + ptr_record->name[MAX_FUNCTION_NAME_LENGTH] = '\0'; + strncpy( ptr_record->params, size_str, MAX_PARAMS_LENGTH ); /* Note: The size string starts with either 'm' or 'c' to indicate 'm'alloc or 'c'alloc */ + ptr_record->params[MAX_PARAMS_LENGTH] = '\0'; + ptr_record->lineno = func_lineno; + ptr_record->block_size = size; + ptr_record->total_block_size += size; + +#ifdef MEM_COUNT_DETAILS + /* Export heap memory allocation record to the .csv file */ + fprintf( fid_csv_filename, "A,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); +#endif + + if ( ptr_record->frame_allocated != -1 ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Attempt to Allocate the Same Memory Block with Freeing it First!" ); + exit( -1 ); + } + + ptr_record->frame_allocated = update_cnt; /* Store the current frame number -> later it will be used to determine the total duration */ + + /* Update Heap Size in the current frame */ + current_heap_size += ptr_record->block_size; + + /* Check, if this is the new Worst-Case RAM (stack + heap) */ + current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) ); + if ( current_stack_size + current_heap_size > wc_ram_size ) + { + wc_ram_size = current_stack_size + current_heap_size; + wc_ram_frame = update_cnt; + } + + /* Add new entry to the heap allocation call tree */ + if ( heap_allocation_call_tree == NULL ) + { + fprintf( stderr, "Error: Heap allocation call tree not created!" ); + exit( -1 ); + } + + /* check, if the maximum size of the call tree has been reached -> resize if so */ + if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size ) + { + heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP; + heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) ); + } + + /* push new entry (positive number means push op, neagtive number means pop op; zero index must be converted to 0.01 :-) */ + heap_allocation_call_tree[heap_allocation_call_tree_size++] = index_record; + + return ptr_record->block_ptr; +} + +/*-------------------------------------------------------------------* + * mem_alloc_block() + * + * Physical allocation of memory using malloc(). Appends 'signature' before and after the block, + * pre-fills memory block with magic value + *--------------------------------------------------------------------*/ + +static void *mem_alloc_block( size_t size, const char *size_str ) +{ + size_t rounded_size; + void *block_ptr; + char *tmp_ptr; + size_t n, f; + int32_t fill_value; + int32_t *ptr32; + int32_t mask, temp; + + /* Round Up Block Size */ + rounded_size = ROUND_BLOCK_SIZE( size ); + + /* Allocate memory using the standard malloc() by adding room for Signature Values */ + block_ptr = malloc( rounded_size + BLOCK_ROUNDING * 2 ); + + if ( block_ptr == NULL ) + { + return NULL; + } + + /* Add Signature Before the Start of the Block */ + ptr32 = (int32_t *) block_ptr; + n = N_32BITS_BLOCKS; + do + { + *ptr32++ = MAGIC_VALUE_OOB; + } while ( --n ); + + /* Fill Memory Block with Magic Value or 0 */ + fill_value = MAGIC_VALUE_USED; + if ( IS_CALLOC( size_str ) ) + { + fill_value = 0x00000000; + } + n = size / sizeof( int32_t ); + while ( n-- ) + { + *ptr32++ = fill_value; + } + + /* Fill the Reminder of the Memory Block - After Rounding */ + n = rounded_size - size; + f = n % sizeof( int32_t ); + if ( f != 0 ) + { + /* when filling with '0' need to adapt the magic value */ + /* shift by [1->24, 2->16, 3->8] */ + mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); /* (1) */ + temp = MAGIC_VALUE_OOB & mask; + if ( fill_value != 0x0 ) + { /* for malloc merge fill value */ + temp += ( ~mask ) & MAGIC_VALUE_USED; + } /* for calloc the code in (1) above already introduces zeros */ + *ptr32++ = temp; + } + n /= sizeof( int32_t ); + n += N_32BITS_BLOCKS; + + /* Add Signature After the End of Block */ + do + { + *ptr32++ = MAGIC_VALUE_OOB; + } while ( --n ); + + /* Adjust the Memory Block Pointer (Magic Value Before and After the Memory Block Requested) */ + tmp_ptr = (char *) block_ptr; + tmp_ptr += BLOCK_ROUNDING; + block_ptr = (void *) tmp_ptr; + + return block_ptr; +} + +/*-------------------------------------------------------------------* + * mem_set_usage() + * + * Calculates actual usage of memory block by checking the magic value that was used to pre-fill + * each memory block during its allocation + *--------------------------------------------------------------------*/ + +static int mem_set_usage( allocator_record *record_ptr ) +{ + int total_bytes_used; + + size_t n; + int32_t *ptr32; + char *ptr8; + size_t total_bytes; + int32_t fill_value; + + fill_value = MAGIC_VALUE_USED; + if ( ( record_ptr->params[0] ) == 'c' ) + { + fill_value = 0x00000000; + } + + total_bytes = record_ptr->block_size; + + /* Check 4 bytes at a time */ + ptr32 = (int32_t *) record_ptr->block_ptr; + total_bytes_used = 0; + for ( n = total_bytes / sizeof( int32_t ); n > 0; n-- ) + { + if ( *ptr32++ != fill_value ) + { + total_bytes_used += sizeof( int32_t ); + } + } + + /* Check remaining bytes (If Applicable) 1 byte at a time */ + ptr8 = (char *) ptr32; + for ( n = total_bytes % sizeof( int32_t ); n > 0; n-- ) + { + if ( *ptr8++ != (char) fill_value ) + { + total_bytes_used++; + } + + /* Update Value */ + fill_value >>= 8; + } + + return total_bytes_used; +} + +/*-------------------------------------------------------------------* + * mem_check_OOB() + * + * Checks, if out-of-bounds access has occured. This is done by inspecting the 'signature' value + * taht has been added before and after the memory block during its allocation + *--------------------------------------------------------------------*/ + +static unsigned int mem_check_OOB( allocator_record *record_ptr ) +{ + int32_t *ptr32; + unsigned int OOB_Flag = 0x0; + int32_t mask; + size_t i; + int f; + + ptr32 = (int32_t *) record_ptr->block_ptr - N_32BITS_BLOCKS; + + /* Check the Signature at the Beginning of Memory Block */ + i = N_32BITS_BLOCKS; + do + { + if ( *ptr32++ ^ MAGIC_VALUE_OOB ) + { + OOB_Flag |= OOB_START; + } + } while ( --i ); + + /* Advance to End (Snap to lowest 32 Bits) */ + ptr32 += record_ptr->block_size / sizeof( int32_t ); + + /* Calculate Unused Space That has been added to get to the rounded Block Size */ + i = ROUND_BLOCK_SIZE( record_ptr->block_size ) - record_ptr->block_size; + + /* Partial Check of Signature at the End of Memory Block (for block size that has been rounded) */ + f = i % sizeof( int32_t ); + if ( f != 0 ) + { + mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); + if ( ( *ptr32++ ^ MAGIC_VALUE_OOB ) & mask ) + { + OOB_Flag |= OOB_END; + } + } + + /* Full Check of Signature at the End of Memory Block, i.e. all 32 Bits (for the remainder after rounding) */ + i /= sizeof( int32_t ); + i += N_32BITS_BLOCKS; + do + { + if ( *ptr32++ ^ MAGIC_VALUE_OOB ) + { + OOB_Flag |= OOB_END; + } + } while ( --i ); + + return OOB_Flag; +} + +/*-------------------------------------------------------------------* + * malloc_hash() + * + * Calculate hash from function name, line number and malloc size + *--------------------------------------------------------------------*/ + +static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str ) +{ + unsigned long hash = 5381; + const char *ptr_str; + + ptr_str = func_name; + while ( ptr_str != NULL && *ptr_str != '\0' ) + { + hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */ + } + + hash = ( ( hash << 5 ) + hash ) + func_lineno; /* hash * 33 + func_lineno */ + + ptr_str = size_str; + while ( ptr_str != NULL && *ptr_str != '\0' ) + { + hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */ + } + + return hash; +} + +/*-------------------------------------------------------------------* + * get_mem_record() + * + * Search for memory record in the internal list, return NULL if not found + * Start from index_record + *--------------------------------------------------------------------*/ + +allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record ) +{ + int i; + + if ( *index_record < 0 || *index_record > Num_Records ) + { + return NULL; + } + + /* calculate hash */ + *hash = malloc_hash( func_name, func_lineno, size_str ); + + for ( i = *index_record; i < Num_Records; i++ ) + { + /* check, if memory block is not allocated at the moment and the hash matches */ + if ( allocation_list[i].block_ptr == NULL && *hash == allocation_list[i].hash ) + { + *index_record = i; + return &( allocation_list[i] ); + } + } + + /* not found */ + *index_record = -1; + return NULL; +} + + +/*-------------------------------------------------------------------* + * mem_free() + * + * This function de-allocatesd the memory block and frees the mphysical memory with free(). + * It also updates actual and average usage of the memory block. + * + * Note: The record is not removed from the list and may be reused later on in mem_alloc()! + *--------------------------------------------------------------------*/ + +void mem_free( const char *func_name, int func_lineno, void *ptr ) +{ + int i, index_record; + char *tmp_ptr; + allocator_record *ptr_record; + + /* Search for the Block Pointer in the List */ + ptr_record = NULL; + index_record = -1; + for ( i = 0; i < Num_Records; i++ ) + { + if ( ptr == allocation_list[i].block_ptr ) + { /* Yes, Found it */ + ptr_record = &( allocation_list[i] ); + index_record = i; + break; + } + } + + if ( ptr_record == NULL ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Unable to Find Record Corresponding to the Allocated Memory Block!" ); + exit( -1 ); + } + + /* Update the Heap Size */ + current_heap_size -= ptr_record->block_size; + + /* Calculate the Actual Usage of the Memory Block (Look for Signature) */ + ptr_record->total_used_size += mem_set_usage( ptr_record ); + + /* Check, if Out-Of-Bounds Access has been Detected */ + ptr_record->OOB_Flag = mem_check_OOB( ptr_record ); + +#ifdef MEM_COUNT_DETAILS + /* Export heap memory de-allocation record to the .csv file */ + fprintf( fid_csv_filename, "D,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); +#endif + + /* De-Allocate Memory Block */ + tmp_ptr = (char *) ptr; + tmp_ptr -= BLOCK_ROUNDING; + ptr = (void *) tmp_ptr; + free( ptr ); + + /* Add new entry to the heap allocation call tree */ + if ( heap_allocation_call_tree == NULL ) + { + fprintf( stderr, "Error: Heap allocation call tree not created!" ); + exit( -1 ); + } + + /* check, if the maximum size of the call tree has been reached -> resize if so */ + if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size ) + { + heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP; + heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) ); + } + + heap_allocation_call_tree[heap_allocation_call_tree_size++] = -index_record; + + /* Reset memory block pointer (this is checked when updating wc intra-frame and inter-frame memory) */ + ptr_record->block_ptr = NULL; + + return; +} + + +/*-------------------------------------------------------------------* + * update_mem() + * + * This function updates the worst-case intra-frame memory and the worst-case inter-frame memory. + *--------------------------------------------------------------------*/ + +void update_mem( void ) +{ + int i, j, flag_alloc = -1, i_record; + int size_current_intra_frame_heap; + int *list_current_intra_frame_heap = NULL, n_items_current_intra_frame_heap; + allocator_record *ptr_record; + + /* process the heap allocation call tree and prepare lists of intra-frame and inter-frame heap memory blocks for this frame */ + n_items_current_intra_frame_heap = 0; + size_current_intra_frame_heap = 0; + for ( i = 0; i < heap_allocation_call_tree_size; i++ ) + { + /* get the record */ + i_record = heap_allocation_call_tree[i]; + + if ( i_record > 0 ) + { + flag_alloc = 1; + } + else if ( i_record < 0 ) + { + flag_alloc = 0; + i_record = -i_record; + } + ptr_record = &( allocation_list[i_record] ); + + if ( ptr_record->frame_allocated == update_cnt && ptr_record->block_ptr == NULL ) + { + /* intra-frame heap memory */ + if ( list_current_intra_frame_heap == NULL ) + { + list_current_intra_frame_heap = (int *) malloc( heap_allocation_call_tree_size * sizeof( int ) ); + memset( list_current_intra_frame_heap, -1, heap_allocation_call_tree_size * sizeof( int ) ); + } + + /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ + if ( i_record == 0 ) + { + flag_alloc = 1; + for ( j = 0; j < n_items_current_intra_frame_heap; j++ ) + { + if ( list_current_intra_frame_heap[j] == i_record ) + { + flag_alloc = 0; + break; + } + } + } + + if ( flag_alloc ) + { + /* add to list */ + list_current_intra_frame_heap[n_items_current_intra_frame_heap++] = i_record; + size_current_intra_frame_heap += ptr_record->block_size; + + /* no need to re-size the list -> the initially allocated size should be large enough */ + } + else + { + /* remove from list */ + for ( j = 0; j < n_items_current_intra_frame_heap; j++ ) + { + if ( list_current_intra_frame_heap[j] == i_record ) + { + break; + } + } + memmove( &list_current_intra_frame_heap[j], &list_current_intra_frame_heap[j + 1], ( n_items_current_intra_frame_heap - j ) * sizeof( int ) ); + n_items_current_intra_frame_heap--; + size_current_intra_frame_heap -= ptr_record->block_size; + + /* reset block size */ + ptr_record->frame_allocated = -1; + ptr_record->block_size = 0; + } + } + else + { + /* inter-frame heap memory */ + + /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ + if ( i_record == 0 ) + { + flag_alloc = 1; + for ( j = 0; j < n_items_current_inter_frame_heap; j++ ) + { + if ( list_current_inter_frame_heap[j] == i_record ) + { + flag_alloc = 0; + break; + } + } + } + + if ( flag_alloc ) + { + /* add to list */ + if ( n_items_current_inter_frame_heap >= max_items_current_inter_frame_heap ) + { + /* resize list, if needed */ + max_items_current_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; + list_current_inter_frame_heap = realloc( list_current_inter_frame_heap, max_items_current_inter_frame_heap * sizeof( int ) ); + } + + list_current_inter_frame_heap[n_items_current_inter_frame_heap++] = i_record; + size_current_inter_frame_heap += ptr_record->block_size; + } + else + { + /* remove from list */ + for ( j = 0; j < n_items_current_inter_frame_heap; j++ ) + { + if ( list_current_inter_frame_heap[j] == i_record ) + { + break; + } + } + memmove( &list_current_inter_frame_heap[j], &list_current_inter_frame_heap[j + 1], ( n_items_current_inter_frame_heap - j ) * sizeof( int ) ); + n_items_current_inter_frame_heap--; + size_current_inter_frame_heap -= ptr_record->block_size; + + /* reset block size */ + ptr_record->frame_allocated = -1; + ptr_record->block_size = 0; + } + } + } + + /* check, if this is the new worst-case for intra-frame heap memory */ + if ( size_current_intra_frame_heap > size_wc_intra_frame_heap ) + { + if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap ) + { + /* resize the list, if needed */ + max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; + list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) ); + } + + /* copy current-frame list to worst-case list */ + memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) ); + n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap; + size_wc_intra_frame_heap = size_current_intra_frame_heap; + location_wc_intra_frame_heap = update_cnt; + + /* update the wc numbers in all individual records */ + for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) + { + i_record = list_wc_intra_frame_heap[i]; + ptr_record = &( allocation_list[i_record] ); + ptr_record->wc_heap_size_intra_frame = ptr_record->block_size; + } + } + + /* check, if this is the new worst-case for inter-frame heap memory */ + if ( size_current_inter_frame_heap > size_wc_inter_frame_heap ) + { + if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap ) + { + /* resize list, if needed */ + max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; + list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) ); + } + + /* copy current-frame list to worst-case list */ + memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) ); + n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap; + size_wc_inter_frame_heap = size_current_inter_frame_heap; + location_wc_inter_frame_heap = update_cnt; + + /* update the wc numbers in all individual records */ + for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) + { + i_record = list_wc_inter_frame_heap[i]; + ptr_record = &( allocation_list[i_record] ); + ptr_record->wc_heap_size_inter_frame = ptr_record->block_size; + } + } + + /* reset heap allocation call tree */ + heap_allocation_call_tree_size = 0; + + /* de-allocate list of intra-frame heap memory blocks in the current fraeme - it's needed only inside this function */ + if ( list_current_intra_frame_heap ) + { + free( list_current_intra_frame_heap ); + } + + return; +} + +#ifdef MEM_COUNT_DETAILS +/*-------------------------------------------------------------------* + * subst() + * + * Substitute character in string + *--------------------------------------------------------------------*/ + +static void subst( char *s, char from, char to ) +{ + while ( *s == from ) + { + *s++ = to; + } + + return; +} + + +/*-------------------------------------------------------------------* + * mem_count_summary() + * + * Print detailed (per-item) information about heap memory usage + *--------------------------------------------------------------------*/ + +static void mem_count_summary( void ) +{ + int i, j, index, index_record; + size_t length; + char buf[300], format_str[50], name_str[MAX_FUNCTION_NAME_LENGTH + 3], parms_str[MAX_PARAMS_LENGTH + 1], type_str[10], usage_str[20], size_str[20], line_str[10]; + allocator_record *ptr_record, *ptr; + + /* Prepare format string */ + sprintf( format_str, "%%-%ds %%5s %%6s %%-%ds %%20s %%6s ", MAX_FUNCTION_NAME_LENGTH, MAX_PARAMS_LENGTH ); + + if ( n_items_wc_intra_frame_heap > 0 ) + { + /* Intra-Frame Heap Size */ + fprintf( stdout, "\nList of memory blocks when maximum intra-frame heap size is reached:\n\n" ); + + /* Find duplicate records (same hash and worst-case heap size) */ + for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) + { + index_record = list_wc_intra_frame_heap[i]; + if ( index_record == -1 ) + { + continue; + } + + ptr_record = &( allocation_list[index_record] ); + for ( j = i + 1; j < n_items_wc_intra_frame_heap; j++ ) + { + index = list_wc_intra_frame_heap[j]; + if ( index == -1 ) + { + continue; + } + ptr = &( allocation_list[index] ); + + if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_intra_frame == ptr_record->wc_heap_size_intra_frame ) + { + ptr_record->noccurances++; + list_wc_intra_frame_heap[j] = -1; + } + } + } + + /* Print Header */ + sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Maximum Size", "Usage" ); + puts( buf ); + length = strlen( buf ); + sprintf( buf, "%0*d\n", (int) length - 1, 0 ); + subst( buf, '0', '-' ); + puts( buf ); + + for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) + { + index_record = list_wc_intra_frame_heap[i]; + + if ( index_record != -1 ) + { + /* get the record */ + ptr_record = &( allocation_list[index_record] ); + + /* prepare information strings */ + strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH ); + strcat( name_str, "()" ); + name_str[MAX_FUNCTION_NAME_LENGTH] = '\0'; + strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH ); + parms_str[MAX_PARAMS_LENGTH] = '\0'; + + if ( ptr_record->params[0] == 'm' ) + { + strcpy( type_str, "malloc" ); + } + else + { + strcpy( type_str, "calloc" ); + } + + sprintf( line_str, "%d", ptr_record->lineno ); + + /* prepare average usage & memory size strings */ + sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 1 ) ) * 100.0f ) ); + + if ( ptr_record->noccurances > 1 ) + { + sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + } + else + { + sprintf( size_str, "%d %s", (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + } + + sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str ); + puts( buf ); + } + } + + fprintf( stdout, "\n" ); + } + + if ( n_items_wc_inter_frame_heap > 0 ) + { + /* Inter-Frame Heap Size */ + fprintf( stdout, "\nList of memory blocks when maximum inter-frame heap size is reached:\n\n" ); + + /* Find duplicate records (same hash and worst-case heap size) */ + for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) + { + index_record = list_wc_inter_frame_heap[i]; + if ( index_record == -1 ) + { + continue; + } + ptr_record = &( allocation_list[index_record] ); + ptr_record->noccurances = 1; /* reset the counter as some blocks may have been both, intra-frame and inter-frame */ + for ( j = i + 1; j < n_items_wc_inter_frame_heap; j++ ) + { + index = list_wc_inter_frame_heap[j]; + if ( index == -1 ) + { + continue; + } + ptr = &( allocation_list[index] ); + + if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_inter_frame == ptr_record->wc_heap_size_inter_frame ) + { + ptr_record->noccurances++; + list_wc_inter_frame_heap[j] = -1; + } + } + } + + /* Print Header */ + sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Memory Size", "Usage" ); + puts( buf ); + length = strlen( buf ); + sprintf( buf, "%0*d\n", (int) length - 1, 0 ); + subst( buf, '0', '-' ); + puts( buf ); + + for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) + { + index_record = list_wc_inter_frame_heap[i]; + + if ( index_record != -1 ) + { + /* get the record */ + ptr_record = &( allocation_list[index_record] ); + + /* prepare information strings */ + strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH ); + strcat( name_str, "()" ); + name_str[MAX_FUNCTION_NAME_LENGTH] = '\0'; + strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH ); + parms_str[MAX_PARAMS_LENGTH] = '\0'; + + if ( ptr_record->params[0] == 'm' ) + { + strcpy( type_str, "malloc" ); + } + else + { + strcpy( type_str, "calloc" ); + } + + sprintf( line_str, "%d", ptr_record->lineno ); + + /* prepare average usage & memory size strings */ + sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 0.1f ) ) * 100.0f + 0.5f ) ); + + if ( ptr_record->noccurances > 1 ) + { + sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + } + else + { + sprintf( size_str, "%d %s", (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + } + + sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str ); + puts( buf ); + } + } + + fprintf( stdout, "\n" ); + } + + return; +} + +#endif + +/*-------------------------------------------------------------------* + * print_mem() + * + * Print information about ROM and RAM memory usage + *--------------------------------------------------------------------*/ + +void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) +{ + int i, nElem; + + fprintf( stdout, "\n\n --- Memory usage --- \n\n" ); + + if ( Const_Data_PROM_Table != NULL ) + { + nElem = 0; + while ( strcmp( Const_Data_PROM_Table[nElem].file_spec, "" ) != 0 ) + nElem++; + + for ( i = 0; i < nElem; i++ ) + { + fprintf( stdout, "Program ROM size (%s): %d instruction words\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size ); + } + + for ( i = 0; i < nElem; i++ ) + { + if ( Const_Data_PROM_Table[i].Get_Const_Data_Size_Func == NULL ) + { + fprintf( stdout, "Error: Cannot retrieve or calculate Table ROM size of (%s)!\n", Const_Data_PROM_Table[i].file_spec ); + } + + fprintf( stdout, "Table ROM (const data) size (%s): %d %s\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].Get_Const_Data_Size_Func() >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); + } + } + else + { + fprintf( stdout, "Program ROM size: not available\n" ); + fprintf( stdout, "Table ROM (const data) size: not available\n" ); + } + + if ( wc_ram_size > 0 ) + { + fprintf( stdout, "Maximum RAM (stack + heap) size: %d %s in frame %d\n", wc_ram_size >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], wc_ram_frame ); + } + else + { + fprintf( stdout, "Maximum RAM (stack + heap) size: not available\n" ); + } + + /* check, if the stack is empty */ + if ( ptr_current_stack != ptr_base_stack ) + { + fprintf( stderr, "Warning: Stack is not empty.\n" ); + } + + if ( ptr_base_stack - ptr_max_stack > 0 ) + { + fprintf( stdout, "Maximum stack size: %lu %s in frame %d\n", ( ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], + wc_stack_frame ); + } + else + { + fprintf( stdout, "Maximum stack size: not available\n" ); + } + + /* last update of intra-frame memory and inter-frame memory, if needed */ + if ( heap_allocation_call_tree_size > 0 ) + { + update_mem(); + } + + /* check, if all memory blocks have been deallocated (freed) */ + for ( i = 0; i < Num_Records; i++ ) + { + if ( allocation_list[i].block_ptr != NULL ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", allocation_list[i].name, allocation_list[i].lineno, "Error: Memory Block has not been De-Allocated with free()!" ); + exit( -1 ); + } + } + + if ( n_items_wc_intra_frame_heap > 0 ) + { + fprintf( stdout, "Maximum intra-frame heap size: %d %s in frame %d\n", size_wc_intra_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_intra_frame_heap ); + } + else + { + fprintf( stdout, "Maximum intra-frame heap size: 0\n" ); + } + + if ( n_items_wc_inter_frame_heap > 0 ) + { + fprintf( stdout, "Maximum inter-frame heap size: %d %s in frame %d\n", size_wc_inter_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_inter_frame_heap ); + } + else + { + fprintf( stdout, "Maximum inter-frame heap size: 0\n" ); + } + +#ifdef MEM_COUNT_DETAILS + /* Print detailed information about worst-case stack usage */ + if ( ptr_base_stack - ptr_max_stack > 0 ) + { + print_stack_call_tree(); + } + + /* Print detailed information about worst-case heap usage */ + mem_count_summary(); +#endif + + if ( Stat_Cnt_Size > 0 ) + { + fprintf( stdout, "\nNote: 1 word = %d bits\n", 8 << Stat_Cnt_Size ); + fprintf( stdout, "This is an optimistic estimate of memory consumption assuming that each variable type is stored with sizeof(type) bits\n" ); + } + + if ( n_items_wc_intra_frame_heap > 0 ) + { + fprintf( stdout, "Intra-frame heap memory is allocated and de-allocated in the same frame\n" ); + } + + /* De-allocate list of heap memory blocks */ + if ( allocation_list != NULL ) + { + free( allocation_list ); + } + + /* De-allocate heap allocation call tree */ + if ( heap_allocation_call_tree != NULL ) + { + free( heap_allocation_call_tree ); + } + + /* De-allocate intra-frame and inter-frame heap lists */ + if ( list_wc_intra_frame_heap != NULL ) + { + free( list_wc_intra_frame_heap ); + } + + if ( list_current_inter_frame_heap != NULL ) + { + free( list_current_inter_frame_heap ); + } + + if ( list_wc_inter_frame_heap != NULL ) + { + free( list_wc_inter_frame_heap ); + } + +#ifdef MEM_COUNT_DETAILS + if ( fid_csv_filename != NULL ) + { + fclose( fid_csv_filename ); + } +#endif + + return; +} + +#endif /* WMOPS */ + +#ifndef WMOPS +int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */ +#endif + + \ No newline at end of file diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index 989be87101839facda4638d75a16b65b1188720d..fda367732897571857b4b48204a6a2e4f99b16ae 100644 GIT binary patch literal 43832 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 YzJ;x|^=xCHJy!i3LGs%4NB-@90T>HoO8@`> literal 42791 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 Lcs+Q{-HHDN%`h}h diff --git a/src/wmc_tool/wmc_tool.cpp b/src/wmc_tool/wmc_tool.cpp index e9300e61..c2fdefcb 100644 --- a/src/wmc_tool/wmc_tool.cpp +++ b/src/wmc_tool/wmc_tool.cpp @@ -82,7 +82,8 @@ static void usage() " -m filename [--rom filename]: add statistics about ROM and RAM consumption\n" " note: filename shall point to a .c file containing the print_mem() function\n" " -b [--no-backup]: no backup of original files\n" - " -c dirname [--generate-wmc-files dirname]: copy wmc_auto.h and wmc_auto.c to a user-specified directory\n\n", + " -c dirname [--generate-wmc-files dirname]: copy wmc_auto.h and wmc_auto.c to a user-specified directory\n", + " -f value [--frames-per-second value]: set the number of frames per second (default 50.0)\n\n", WMC_TOOL_VERSION_NO, VERSION_STL); return; @@ -95,7 +96,8 @@ static TOOL_ERROR Parse_Command_Line( int* Operation, unsigned int* Tool_Warning_Mask, char* Const_Data_PROM_File, - char* wmops_output_dir + char* wmops_output_dir, + float *frames_per_sec ) { int i; @@ -126,6 +128,8 @@ static TOOL_ERROR Parse_Command_Line( /* Parse all options */ *i_last_cmd__line_option = 0; + *frames_per_sec = 50.0; + i = 0; while (i < nargs && *args[i] == '-') { @@ -221,12 +225,24 @@ static TOOL_ERROR Parse_Command_Line( *Operation |= OUTPUT_WMOPS_FILES; } + else if (_stricmp(arg_name, "f") == 0 || _stricmp(arg_name, "frames-per-second") == 0) + { + /* get the next argument - must be a positive float number */ + i++; + + /* get the value */ + if ((*frames_per_sec = strtof(args[i], NULL)) <= 0.0 ) + { + fprintf(stderr, "Incorrect number of frames per second specified: %s!\n\n", args[i]); + return ERR_CMD_LINE; + } + } /* Move to the next argument */ i++; } - /* return the position of the first non-option argument */ + /* return the position of the first non-optional argument */ *i_last_cmd__line_option = i; return NO_ERR; @@ -1008,75 +1024,90 @@ static TOOL_ERROR Process_File( * Output_Wmops_File *-------------------------------------------------------------------*/ -static TOOL_ERROR Output_Wmops_File( char *PathName, bool Backup ) +static TOOL_ERROR Output_Wmops_File( char *PathName, float frames_per_sec, bool Backup ) { TOOL_ERROR ErrCode = NO_ERR; - FILE *file; - int i; - size_t file_size; - char LongFileName[MAX_PATH + 1]; /* +1 for NUL Char*/ + FILE *TargetFile; + int i, len, size; + char SrcFileName[MAX_PATH + 1]; /* +1 for NUL Char*/ + char TargetFileName[MAX_PATH + 1]; /* +1 for NUL Char*/ + char *text = NULL, *p_str, temp_str[50]; + const char* wmops_auto_files[] = { "wmc_auto.h", "wmc_auto.c" }; static const char wmops_auto_file_h[] = #include "wmc_auto_h.txt" ; static const char wmops_auto_file_c[] = #include "wmc_auto_c.txt" ; + for (i = 0; i < 2; i++) { - if (i == 0) + /* Copy the source file contents to a char * buffer for further manipulation */ + size = i == 0 ? sizeof(wmops_auto_file_h) : sizeof(wmops_auto_file_c); + + text = (char*)calloc(size, sizeof(char)); + if (text == NULL) { - /* Create Output Filename */ - strcpy(LongFileName, PathName); - strcat(LongFileName, "/wmc_auto.h"); + ErrCode = ERR_FILE_READ; + Error("Error reading file " DQUOTE("%s"), ErrCode, SrcFileName); + goto ret; + } - /* Try opening the target file wmc_auto.h */ - if ((file = fopen(LongFileName, "wb")) == NULL) - { - ErrCode = ERR_FILE_OPEN; - Error("Cannot Open " DQUOTE("%s"), ErrCode, "wmc_auto.h"); - goto ret; - } + strncpy(text, i == 0 ? wmops_auto_file_h : wmops_auto_file_c, size); - /* Write it */ - file_size = sizeof(wmops_auto_file_h) - 1; - if (fwrite(wmops_auto_file_h, 1, file_size, file) != file_size) - { - ErrCode = Write_Error(LongFileName); - goto ret; - } + /* Replace the constant FRAMES_PER_SECOND */ + p_str = strstr(text, "#define FRAMES_PER_SECOND 50.0"); - /* Close the File */ - fclose(file); - } - else + if (p_str != NULL) { - /* Create Output Filename */ - strcpy(LongFileName, PathName); - strcat(LongFileName, "/wmc_auto.c"); + sprintf(temp_str, "#define FRAMES_PER_SECOND %.1f", frames_per_sec); + strncpy(p_str, temp_str, sizeof(temp_str)); + } - /* Try opening the target file wmc_auto.c */ - if ((file = fopen(LongFileName, "wb")) == NULL) - { - ErrCode = ERR_FILE_OPEN; - Error("Cannot Open " DQUOTE("%s"), ErrCode, "wmc_auto.c"); - goto ret; - } + /* Create Target Filename */ + strcpy(TargetFileName, PathName); + + /* strip off surrounding quotes "" */ + strip(TargetFileName, '"'); + + /* Replace '\' Windows Directory Delimiter by UNIX '/' */ + strcsub(TargetFileName, '\\', '/'); - /* Write it */ - file_size = sizeof(wmops_auto_file_c) - 1; - if (fwrite(wmops_auto_file_c, 1, file_size, file) != file_size) + /* check that there is a proper trailing '/' */ + len = (int)strlen(TargetFileName); + if (TargetFileName[len - 1] != '/') + { + /* add a trailing '/' */ + strcat(TargetFileName, "/"); + } + else + { + /* remove all extra trailing '/' */ + while (TargetFileName[len - 2] == '/') { - ErrCode = Write_Error(LongFileName); - goto ret; + TargetFileName[len - 1] = '\0'; + len--; } + } + strcat(TargetFileName, wmops_auto_files[i]); - /* Close the File */ - fclose(file); + /* Try opening the target file */ + if ((TargetFile = fopen(TargetFileName, "wb")) == NULL) + { + ErrCode = ERR_FILE_OPEN; + Error("Cannot Open " DQUOTE("%s"), ErrCode, TargetFileName); + goto ret; } + + /* Write the whole content to the target file */ + fwrite(text, size, 1, TargetFile); + fclose(TargetFile); + free(text); } ret: + return ErrCode; } @@ -1096,6 +1127,7 @@ int main( int argc, char *argv[] ) char LongFileName[MAX_PATH]; char Const_Data_PROM_File[MAX_PATH] = ""; char wmops_output_dir[MAX_PATH]; + float frames_per_sec; T_FILE_BOOK file_book[MAX_RECORDS]; struct stat s; Parse_Context_def ParseContext; @@ -1122,7 +1154,7 @@ int main( int argc, char *argv[] ) if ( ( ErrCode = Parse_Command_Line( argc, argv, &i_cmd_line, &Operation, &Tool_Warning_Mask, Const_Data_PROM_File, - wmops_output_dir) ) != NO_ERR ) + wmops_output_dir, &frames_per_sec) ) != NO_ERR ) { if (ErrCode == ERR_HELP) { @@ -1254,6 +1286,7 @@ int main( int argc, char *argv[] ) { /* it's an existing directory without file mask -> use *.c as default */ strcpy(file_book[i].pathname, file_book[i].cmd_line_spec); + /* remove the trailing '/', if any */ size = (int)strlen(file_book[i].pathname); while (file_book[i].pathname[size - 1] == '/') @@ -1409,7 +1442,7 @@ int main( int argc, char *argv[] ) if (Operation & OUTPUT_WMOPS_FILES) { /* Yes */ - if ((ErrCode = Output_Wmops_File(wmops_output_dir, (Operation & NO_BACKUP) == 0)) != NO_ERR) + if ((ErrCode = Output_Wmops_File(wmops_output_dir, frames_per_sec, (Operation & NO_BACKUP) == 0)) != NO_ERR) { goto ret; } From 31acbb737499a5871e564315ba0f3511dbc2b931 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 14 Sep 2023 15:42:54 +0200 Subject: [PATCH 16/53] prevent calling twice push_wmops() when WMOPS_DETAILS is activated --- src/wmc_tool/test_data/ref/wmc_auto.c | 54 ++++++++++++------------- src/wmc_tool/test_data/ref/wmc_auto.h | Bin 43832 -> 43846 bytes src/wmc_tool/wmc_auto_c.txt | 55 +++++++++++++------------- src/wmc_tool/wmc_auto_h.txt | 2 +- 4 files changed, 56 insertions(+), 55 deletions(-) diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index aefb6963..955c7cab 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -1,5 +1,5 @@ /* - * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. + * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. * * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file @@ -349,28 +349,40 @@ void update_wmops( void ) void print_wmops( void ) { - int i; + int i, label_len, max_label_len; - char *sfmts = "%20s %8s %8s %7s %7s\n"; - char *dfmts = "%20s %8.2f %8.3f %7.3f %7.3f\n"; - char *sfmt = "%20s %8s %8s %7s %7s %7s %7s %7s\n"; - char *dfmt = "%20s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\n"; + char *sfmts = "%*s %8s %8s %7s %7s\n"; + char *dfmts = "%*s %8.2f %8.3f %7.3f %7.3f\n"; + char *sfmt = "%*s %8s %8s %7s %7s %7s %7s %7s\n"; + char *dfmt = "%*s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\n"; #ifdef WMOPS_WC_FRAME_ANALYSIS - int j, label_len, max_label_len; + int j; char *sfmtt = "%20s %4s %15s\n"; char *dfmtt = "%20s %4d "; #endif - fprintf( stdout, "\n\n --- Complexity analysis [WMOPS] --- \n\n" ); + /* calculate maximum label length for compact prinout */ + max_label_len = 0; + for ( i = 0; i < num_records; i++ ) + { + label_len = strlen( wmops[i].label ); + if ( label_len > max_label_len ) + { + max_label_len = label_len; + } + } + max_label_len += 4; - fprintf( stdout, "%54s %23s\n", "|------ SELF ------|", "|--- CUMULATIVE ---|" ); - fprintf( stdout, sfmt, " routine", " calls", " min ", " max ", " avg ", " min ", " max ", " avg " ); - fprintf( stdout, sfmt, "---------------", "------", "------", "------", "------", "------", "------", "------" ); + fprintf( stdout, "\n\n --- Complexity analysis [WMOPS] --- \n\n" ); + + fprintf( stdout, "%*s %33s %23s\n", max_label_len, "", "|------ SELF ------|", "|--- CUMULATIVE ---|" ); + fprintf( stdout, sfmt, max_label_len, " routine", " calls", " min ", " max ", " avg ", " min ", " max ", " avg " ); + fprintf( stdout, sfmt, max_label_len, "---------------", "------", "------", "------", "------", "------", "------", "------" ); for ( i = 0; i < num_records; i++ ) { - fprintf( stdout, dfmt, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt, + fprintf( stdout, dfmt, max_label_len, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt, wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt, FAC * wmops[i].max_selfcnt, wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_selfcnt / wmops[i].update_cnt, @@ -379,25 +391,13 @@ void print_wmops( void ) wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_cnt / wmops[i].update_cnt ); } - fprintf( stdout, sfmts, "---------------", "------", "------", "------", "------" ); - fprintf( stdout, dfmts, "total", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt ); + fprintf( stdout, sfmts, max_label_len, "---------------", "------", "------", "------", "------" ); + fprintf( stdout, dfmts, max_label_len, "total", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt ); fprintf( stdout, "\n" ); #ifdef WMOPS_WC_FRAME_ANALYSIS - /* calculate maximum label length for compact prinout */ - max_label_len = 0; - for ( i = 0; i < num_records; i++ ) - { - label_len = strlen( wmops[i].label ); - if ( label_len > max_label_len ) - { - max_label_len = label_len; - } - } - max_label_len += 4; - fprintf( stdout, "\nComplexity analysis for the worst-case frame %ld:\n", fnum_cnt_wc ); - fprintf( stdout, "%*s %8s %10s %12s\n", max_label_len, " routine", " calls", " SELF", " CUMULATIVE" ); + fprintf( stdout, "%*s %8s %10s %12s\n", max_label_len, " routine", " calls", " SELF", " CUMULATIVE" ); fprintf( stdout, "%*s %8s %10s %10s\n", max_label_len, "---------------", "------", "------", "----------" ); for ( i = 0; i < num_records; i++ ) diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index fda367732897571857b4b48204a6a2e4f99b16ae..4d7eee8275d3d876b1acd9d25bbe7d38d98bafb8 100644 GIT binary patch delta 28 kcmdmSjp^7mrVXE#^C~Gshx\r\n" "#endif\r\n" "\r\n" + "#include \"wmc_auto.h\"\r\n" "\r\n" "\r\n" @@ -349,28 +350,40 @@ "\r\n" "void print_wmops( void )\r\n" "{\r\n" -" int i;\r\n" +" int i, label_len, max_label_len;\r\n" "\r\n" -" char *sfmts = \"%20s %8s %8s %7s %7s\\n\";\r\n" -" char *dfmts = \"%20s %8.2f %8.3f %7.3f %7.3f\\n\";\r\n" -" char *sfmt = \"%20s %8s %8s %7s %7s %7s %7s %7s\\n\";\r\n" -" char *dfmt = \"%20s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\\n\";\r\n" +" char *sfmts = \"%*s %8s %8s %7s %7s\\n\";\r\n" +" char *dfmts = \"%*s %8.2f %8.3f %7.3f %7.3f\\n\";\r\n" +" char *sfmt = \"%*s %8s %8s %7s %7s %7s %7s %7s\\n\";\r\n" +" char *dfmt = \"%*s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\\n\";\r\n" "\r\n" "#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" int j, label_len, max_label_len;\r\n" +" int j;\r\n" " char *sfmtt = \"%20s %4s %15s\\n\";\r\n" " char *dfmtt = \"%20s %4d \";\r\n" "#endif\r\n" "\r\n" -" fprintf( stdout, \"\\n\\n --- Complexity analysis [WMOPS] --- \\n\\n\" );\r\n" +" /* calculate maximum label length for compact prinout */\r\n" +" max_label_len = 0;\r\n" +" for ( i = 0; i < num_records; i++ )\r\n" +" {\r\n" +" label_len = strlen( wmops[i].label );\r\n" +" if ( label_len > max_label_len )\r\n" +" {\r\n" +" max_label_len = label_len;\r\n" +" }\r\n" +" }\r\n" +" max_label_len += 4;\r\n" "\r\n" -" fprintf( stdout, \"%54s %23s\\n\", \"|------ SELF ------|\", \"|--- CUMULATIVE ---|\" );\r\n" -" fprintf( stdout, sfmt, \" routine\", \" calls\", \" min \", \" max \", \" avg \", \" min \", \" max \", \" avg \" );\r\n" -" fprintf( stdout, sfmt, \"---------------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\" );\r\n" +" fprintf( stdout, \"\\n\\n --- Complexity analysis [WMOPS] --- \\n\\n\" );\r\n" +" \r\n" +" fprintf( stdout, \"%*s %33s %23s\\n\", max_label_len, \"\", \"|------ SELF ------|\", \"|--- CUMULATIVE ---|\" );\r\n" +" fprintf( stdout, sfmt, max_label_len, \" routine\", \" calls\", \" min \", \" max \", \" avg \", \" min \", \" max \", \" avg \" );\r\n" +" fprintf( stdout, sfmt, max_label_len, \"---------------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\" );\r\n" "\r\n" " for ( i = 0; i < num_records; i++ )\r\n" " {\r\n" -" fprintf( stdout, dfmt, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt,\r\n" +" fprintf( stdout, dfmt, max_label_len, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt,\r\n" " wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt,\r\n" " FAC * wmops[i].max_selfcnt,\r\n" " wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_selfcnt / wmops[i].update_cnt,\r\n" @@ -379,25 +392,13 @@ " wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_cnt / wmops[i].update_cnt );\r\n" " }\r\n" "\r\n" -" fprintf( stdout, sfmts, \"---------------\", \"------\", \"------\", \"------\", \"------\" );\r\n" -" fprintf( stdout, dfmts, \"total\", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt );\r\n" +" fprintf( stdout, sfmts, max_label_len, \"---------------\", \"------\", \"------\", \"------\", \"------\" );\r\n" +" fprintf( stdout, dfmts, max_label_len, \"total\", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt );\r\n" " fprintf( stdout, \"\\n\" );\r\n" "\r\n" "#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" /* calculate maximum label length for compact prinout */\r\n" -" max_label_len = 0;\r\n" -" for ( i = 0; i < num_records; i++ )\r\n" -" {\r\n" -" label_len = strlen( wmops[i].label );\r\n" -" if ( label_len > max_label_len )\r\n" -" {\r\n" -" max_label_len = label_len;\r\n" -" }\r\n" -" }\r\n" -" max_label_len += 4;\r\n" -"\r\n" " fprintf( stdout, \"\\nComplexity analysis for the worst-case frame %ld:\\n\", fnum_cnt_wc );\r\n" -" fprintf( stdout, \"%*s %8s %10s %12s\\n\", max_label_len, \" routine\", \" calls\", \" SELF\", \" CUMULATIVE\" );\r\n" +" fprintf( stdout, \"%*s %8s %10s %12s\\n\", max_label_len, \" routine\", \" calls\", \" SELF\", \" CUMULATIVE\" );\r\n" " fprintf( stdout, \"%*s %8s %10s %10s\\n\", max_label_len, \"---------------\", \"------\", \"------\", \"----------\" );\r\n" "\r\n" " for ( i = 0; i < num_records; i++ )\r\n" diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index 8284b88a..cfd4a71a 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -1012,7 +1012,7 @@ "int pop_stack( const char *filename, const char *fctname );\r\n" "\r\n" "#ifdef WMOPS_DETAIL\r\n" -"#define STACK_DEPTH_FCT_CALL ( push_wmops( __FUNCTION__ ), push_stack( __FILE__, __FUNCTION__ ) ) /* add push_wmops() in all function calls */\r\n" +"#define STACK_DEPTH_FCT_CALL ( push_wmops( __FUNCTION__ \" [WMC_AUTO]\" ), push_stack( __FILE__, __FUNCTION__ ) ) /* add push_wmops() in all function calls */\r\n" "#define STACK_DEPTH_FCT_RETURN ( pop_wmops(), pop_stack( __FILE__, __FUNCTION__ ) ) /* add pop_wmops() in all function returns */\r\n" "#else\r\n" "#define STACK_DEPTH_FCT_CALL push_stack( __FILE__, __FUNCTION__ )\r\n" From c1d79a59d250ef62940e838daa88ac10145117d4 Mon Sep 17 00:00:00 2001 From: malenov Date: Fri, 15 Sep 2023 09:49:38 +0200 Subject: [PATCH 17/53] raise error if matching #undef WMC_TOOL_SKIP cannot be found --- src/wmc_tool/c_parser.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index f6bc28a9..1dfae62b 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -6438,8 +6438,14 @@ TOOL_ERROR Setup_Regions( if ( ( end = Find_Identifier( ptr, WMC_TOOL_SKIP_STRING, ParseTbl_ptr, ITEM_PREPROC_ARGS | ITEM_PREPROC_UNDEF, ITEM_ENCLOSED, &idx ) ) == NULL ) { /* No */ + + /* Error - #undef WMC_TOOL_SKIP missing ! */ + ErrCode = ERR_EXPECTED_EOS; + Error("Unable to find matching #undef %s!", ErrCode, WMC_TOOL_SKIP_STRING); + goto ret; + /* Skipped Regon will End at EOF */ - end = file_ptr->Data + file_ptr->Size; + //end = file_ptr->Data + file_ptr->Size; } else { /* Yes */ From 7851f8d4e73e311105d6739567154f30dda2e96c Mon Sep 17 00:00:00 2001 From: malenov Date: Fri, 15 Sep 2023 10:34:48 +0200 Subject: [PATCH 18/53] Python script for graphical analysis of the memory allocation tree based on the .csv memory output file generated by the WMC tool --- src/wmc_tool/mem_analysis.py | 130 +++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/wmc_tool/mem_analysis.py diff --git a/src/wmc_tool/mem_analysis.py b/src/wmc_tool/mem_analysis.py new file mode 100644 index 00000000..20b239d4 --- /dev/null +++ b/src/wmc_tool/mem_analysis.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 + +# (C) 2023 copyright VoiceAge Corporation. All Rights Reserved. +# +# This software is protected by copyright law and by international treaties. The source code, and all of its derivations, +# is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file +# or refer to ITU-T Recommendation G.191 on "SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS". +# +# Any use of this software is permitted provided that this notice is not removed and that neither the authors nor +# VoiceAge Corporation are deemed to have made any representations as to the suitability of this software +# for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE. +# +# Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca) + +# This script provides graphical analysis of the memory allocation tree based on the .csv memory output file generated by the WMC tool +# +# usage: python3 mem_analysis.py mem_analysis.csv +# +# where mem_analysis.csv is the per-frame and per-block memory allocation output file generated by the WMC tool +# by activating the MEM_COUNT_DETAILS macro + +import os +import argparse +import csv +import sys +import struct +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt + + +# set matplotlib in interactive mode (plots figure and return control) +# plt.ion() +# plt.close('all') + +parser = argparse.ArgumentParser(description='Analyze memory output file') +parser.add_argument('input',type=str,help='Memory analysis file (e.g. mem_analysis.csv)') +args = parser.parse_args() +fileIn = args.input + +# read input .csv file +df = pd.read_csv(fileIn, header=None, names=['action_type', 'frame', 'name', 'line_no', 'mem_size']) +Nframes = max(df['frame']) + +# merge identical memory blocks -> create new column 'count' +df = df.groupby(df.columns.tolist(),as_index=False).size() +df = df.rename(columns={'size': 'count'}) + +# remove column 'action_type' +df.drop(columns = ['action_type']) + +# merge records with the same signature but different frame number -> create list of frames (sort in ascending order) +df = df.groupby(['name', 'line_no', 'mem_size', 'count'],as_index=False).agg({'frame': list}) +df['frame'] = df['frame'].apply(np.sort) +df['list_len'] = df['frame'].apply(len) # auxiliary column -> length of the list + +# merge records with the same name and frame but different line number -> sum memory sizes and counts +df = df.groupby(['name', 'list_len'],as_index=False).agg({'line_no': list, 'mem_size': sum, 'count':sum, 'frame':'first'}) + +# sort by memory size (put inter-frame memory blocks to the bottom) +select_inter = (df['list_len'] == 2) & (df.apply(lambda row : row['frame'][-1] - row['frame'][0], axis = 1) >= Nframes) +df_inter = df[select_inter] +df_inter = df_inter.sort_values(by=['mem_size'], ascending=False) +df_intra = df[~select_inter] +df_intra = df_intra.sort_values(by=['list_len'], ascending=True) + +# plot inter-frame memory blocks in horizontal bar graph (use .broken_barh method) +fig, ax = plt.subplots() +colors = iter(plt.cm.rainbow(np.linspace(0, 1, len(df.index)))) +y_ticks = [] +y_tick_labels = [] +y_low = 1 +for index, row in df_inter.iterrows(): + l = f"{row['name']} on lines: {row['line_no']}, total memory size: {row['count']}x{row['mem_size']}" + fms = row['frame'].reshape((-1,2)) # convert frame numbers to two-column format + fms[:,1] -= fms[:,0] # second column should now represent the duration of the segment + fmsT = fms.T + fms_list = list(zip(fmsT[0], fmsT[1])) + c = next(colors) + ax.broken_barh(xranges=fms_list, yrange=(y_low, row['count']), label=l, color=c) + y_ticks.append(y_low + 0.5 * row['count']) + y_tick_labels.append(row['name']) + y_low += row['count'] + 0.5 + print(l + f", {row['count'] * fms.shape[0]/Nframes:.3f} mallocs per frame") + +# plot intra-frame memory blocks in horizontal bar graph (use .broken_barh method) +df_intra = df_intra.groupby(['name'],as_index=False).agg({'list_len': list, 'line_no': list, 'mem_size': list, 'count': list, 'frame': list}) +for index, row in df_intra.iterrows(): + # repeat for all grouped memory records in the row + for j in range(len(row['list_len'])): + l = f"{row['name']} on lines: {row['line_no'][j]}, total memory size: {row['count'][j]}x{row['mem_size'][j]}" + fms = row['frame'][j].astype(float).reshape((-1,2)) # convert frame numbers to two-column format + fms[:,1] -= fms[:,0] # second column should now represent the duration of the segment + idx_zero = fms[:,1] == 0 # find zero-length segments -> these are intra-frame memory blocks + fms[idx_zero, 0] += 0.1 + fms[idx_zero, 1] = 0.8 + fmsT = fms.T + fms_list = list(zip(fmsT[0], fmsT[1])) + c = next(colors) + ax.broken_barh(xranges=fms_list, yrange=(y_low, row['count'][j]), label=l, color=c, alpha=0.6) + print(l + f", {row['count'][j] * fms.shape[0]/Nframes:.3f} mallocs per frame") + y_ticks.append(y_low + 0.5 * max(row['count'])) + y_tick_labels.append(row['name']) + y_low += max(row['count']) + 0.5 + +ax.set_yticks(y_ticks) +ax.set_yticklabels(y_tick_labels) +ax.set_ylabel('Memory size') +ax.set_xlabel('Frame') +ax.set_title('Memory analysis') + +# shrink current axis's height by 20% on the bottom to fit the legend +box = ax.get_position() +ax.set_position([box.x0, box.y0 + box.height * 0.2, box.width, box.height * 0.8]) + +# insert the legend below the graph +ax.legend(loc="upper left", bbox_to_anchor=(0, -0.1), ncol=2, borderaxespad=0, fontsize='small') + +# magnify to "almost" fullscreen size (select the alternative for your backend) +fig = plt.gcf() +fig.set_size_inches(15, 9) # we need to set the size manually, otherwise savefig will save the figure in the default size +mng = plt.get_current_fig_manager() +mng.full_screen_toggle() +# mng.frame.Maximize(True) +# mng.window.showMaximized() +# mng.window.state('zoomed') + +# show and save the figure (use block=True to wait until Figure is closed manually) +plt.savefig(os.path.splitext(args.input)[0] + '.png', dpi=100, bbox_inches='tight') +plt.show(block=True) From dc6fdd8af5aedcadf014077341e83c1dd3d86032 Mon Sep 17 00:00:00 2001 From: malenov Date: Fri, 15 Sep 2023 10:48:31 +0200 Subject: [PATCH 19/53] replace printf() with fprintf() for better portability --- src/wmc_tool/c_parser.cpp | 59 ++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index 1dfae62b..a37863e3 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -4105,6 +4105,7 @@ static TOOL_ERROR Find_Constants( memset( str, 0, sizeof( str ) ); memmove( str, ptr2 - LEN, LEN ); memmove( str + LEN + 1, ptr, LEN ); + for ( temp = 0; temp < sizeof( str ); temp++ ) { if ( IS_EOL_CHAR( str[temp] ) || IS_RESERVED_CHAR( str[temp] ) ) @@ -4112,8 +4113,8 @@ static TOOL_ERROR Find_Constants( str[temp] = ' '; } } - printf( "%s: Number=%s#%s#\n", ptr2 == ptr4 ? "+/- is Oper" : "+/- is Cste", - str, memstr( ptr2, ptr ) /*, str+LEN+1*/ ); + + fprintf( stdout, "%s: Number=%s#%s#\n", ptr2 == ptr4 ? "+/- is Oper" : "+/- is Cste", str, memstr( ptr2, ptr ) /*, str+LEN+1*/ ); } #endif } @@ -8692,14 +8693,14 @@ static void Print_Words( const char *words, bool ds = false, int margin = 4 ) /* Terminate String */ temp[margin + lin_length] = NUL_CHAR; /* Print */ - printf("%s", temp); + fprintf(stdout, "%s", temp); /* Advance */ ptr += lin_length; /* Update Remaining Length */ str_length -= lin_length; } while ( str_length > 0 ); /* Print newline */ - printf( "\n" ); + fprintf(stdout, "\n" ); } } @@ -8710,34 +8711,34 @@ void Print_Information( void ) { int i; - printf( "\n Manual Macros Removed during Desintrumentation:\n" ); + fprintf(stdout, "\n Manual Macros Removed during Desintrumentation:\n" ); Print_Words( MANUAL_COUNTING_MACROS_STRING ); - printf( "\n WMOPS Library Functions that are not Instrumented:\n" ); + fprintf(stdout, "\n WMOPS Library Functions that are not Instrumented:\n" ); Print_Words( WMOPS_COUNTLIB_FUNCTS_STRING, true ); - printf( "\n Other Counting Functions that are not Instrumented:\n" ); + fprintf(stdout, "\n Other Counting Functions that are not Instrumented:\n" ); Print_Words( OTHER_COUNTLIB_FUNCTS_STRING ); - printf( "\n WMC_Tool Functions that are not Instrumented:\n" ); + fprintf(stdout, "\n WMC_Tool Functions that are not Instrumented:\n" ); Print_Words( TOOL_FUNCTS_STRING, true ); - printf( "\n System Functions that are not Instrumented:\n" ); + fprintf(stdout, "\n System Functions that are not Instrumented:\n" ); Print_Words( SYSTEM_FUNCTS_STRING ); - printf( "\n System Functions that are Instrumented:\n" ); + fprintf(stdout, "\n System Functions that are Instrumented:\n" ); Print_Words( SYSTEM_ALLOC_FUNCTS_STRING ); - printf( "\n Preprocessor Directives that are Ignored:\n" ); + fprintf(stdout, "\n Preprocessor Directives that are Ignored:\n" ); for (i = 0; Conditionnal_Directives[i] != NULL; i++) { - printf(" %c%s\n", PREPROC_CHAR, Conditionnal_Directives[i]); + fprintf(stdout, " %c%s\n", PREPROC_CHAR, Conditionnal_Directives[i]); } - printf( " %c%s\n", PREPROC_CHAR, Undefine_Directive ); + fprintf(stdout, " %c%s\n", PREPROC_CHAR, Undefine_Directive ); for (i = 0; Other_Directives[i] != NULL; i++) { - printf(" %c%s\n", PREPROC_CHAR, Other_Directives[i]); + fprintf(stdout, " %c%s\n", PREPROC_CHAR, Other_Directives[i]); } - printf( "\n Math Functions that are Instrumented:\n" ); + fprintf(stdout, "\n Math Functions that are Instrumented:\n" ); Print_Words( MATH_FUNCTS_STRING ); - printf( "\n Keywords that are Instrumented:\n" ); + fprintf(stdout, "\n Keywords that are Instrumented:\n" ); for ( i = 0; i < (int)(nitems( keywords )); i++ ) { if ( !( keywords[i].kw_type & ITEM_SKIPPED ) ) @@ -8746,7 +8747,7 @@ void Print_Information( void ) } } - printf( "\n Keywords that are not Instrumented:\n" ); + fprintf(stdout, "\n Keywords that are not Instrumented:\n" ); for ( i = 0; i < (int)(nitems( keywords )); i++ ) { if ( keywords[i].kw_type & ITEM_SKIPPED ) @@ -8755,22 +8756,22 @@ void Print_Information( void ) } } - printf( "\n Recognized Data Types:\n" ); + fprintf(stdout, "\n Recognized Data Types:\n" ); Print_Words( TYPES_STRING ); - printf( "\n Macros used to Instrument: \n" ); - printf( " switch/case: %s(n)\n", AUTO_CASE_COUNTING_MACRO_STRING ); - printf( " logical operators: %s\n", LOGICAL_INSTRUMENT_STRING ); - printf( " ternary operators: %s\n", TERNARY_INSTRUMENT_STRING ); - printf( " all other operators: %s(\"ops_string\")\n", AUTO_DEFAULT_COUNTING_MACRO_STRING ); + fprintf(stdout, "\n Macros used to Instrument: \n" ); + fprintf(stdout, " switch/case: %s(n)\n", AUTO_CASE_COUNTING_MACRO_STRING ); + fprintf(stdout, " logical operators: %s\n", LOGICAL_INSTRUMENT_STRING ); + fprintf(stdout, " ternary operators: %s\n", TERNARY_INSTRUMENT_STRING ); + fprintf(stdout, " all other operators: %s(\"ops_string\")\n", AUTO_DEFAULT_COUNTING_MACRO_STRING ); - printf( "\n Include File Required in Instrumented Source Files:\n" ); - printf( " %s\n", WMOPS_LIB_INCLUDE_STRING ); + fprintf(stdout, "\n Include File Required in Instrumented Source Files:\n" ); + fprintf(stdout, " %s\n", WMOPS_LIB_INCLUDE_STRING ); - printf( "\n Skip Instrumentation:\n" ); - printf( " Begin Uninstrumented Segment: #define " WMC_TOOL_SKIP_STRING "\n" ); - printf( " End of Uninstrumented Segment: #undef " WMC_TOOL_SKIP_STRING "\n" ); - printf( "\n" ); + fprintf(stdout, "\n Skip Instrumentation:\n" ); + fprintf(stdout, " Begin Uninstrumented Segment: #define " WMC_TOOL_SKIP_STRING "\n" ); + fprintf(stdout, " End of Uninstrumented Segment: #undef " WMC_TOOL_SKIP_STRING "\n" ); + fprintf(stdout, "\n" ); } /*-------------------------------------------------------------------* From 51007bc3f263890586509795225cda281e75106a Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 19 Sep 2023 15:24:52 +0200 Subject: [PATCH 20/53] fix an EOL problem whjen replacing #define FRAMES_PER_SECOND --- src/wmc_tool/wmc_tool.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/wmc_tool/wmc_tool.cpp b/src/wmc_tool/wmc_tool.cpp index c2fdefcb..0fbc89bd 100644 --- a/src/wmc_tool/wmc_tool.cpp +++ b/src/wmc_tool/wmc_tool.cpp @@ -1028,7 +1028,7 @@ static TOOL_ERROR Output_Wmops_File( char *PathName, float frames_per_sec, bool { TOOL_ERROR ErrCode = NO_ERR; FILE *TargetFile; - int i, len, size; + int i, len; char SrcFileName[MAX_PATH + 1]; /* +1 for NUL Char*/ char TargetFileName[MAX_PATH + 1]; /* +1 for NUL Char*/ char *text = NULL, *p_str, temp_str[50]; @@ -1043,10 +1043,8 @@ static TOOL_ERROR Output_Wmops_File( char *PathName, float frames_per_sec, bool for (i = 0; i < 2; i++) { - /* Copy the source file contents to a char * buffer for further manipulation */ - size = i == 0 ? sizeof(wmops_auto_file_h) : sizeof(wmops_auto_file_c); - - text = (char*)calloc(size, sizeof(char)); + /* Allocate a char * buffer for further manipulation of the source file text */ + text = (char*)calloc(i == 0 ? sizeof(wmops_auto_file_h) : sizeof(wmops_auto_file_c), sizeof(char)); if (text == NULL) { ErrCode = ERR_FILE_READ; @@ -1054,15 +1052,16 @@ static TOOL_ERROR Output_Wmops_File( char *PathName, float frames_per_sec, bool goto ret; } - strncpy(text, i == 0 ? wmops_auto_file_h : wmops_auto_file_c, size); + /* copy the source file contents to a char * buffer for further manipulation */ + strcpy(text, i == 0 ? wmops_auto_file_h : wmops_auto_file_c); /* Replace the constant FRAMES_PER_SECOND */ - p_str = strstr(text, "#define FRAMES_PER_SECOND 50.0"); + p_str = strstr(text, "#define FRAMES_PER_SECOND"); if (p_str != NULL) { - sprintf(temp_str, "#define FRAMES_PER_SECOND %.1f", frames_per_sec); - strncpy(p_str, temp_str, sizeof(temp_str)); + sprintf(temp_str, "#define FRAMES_PER_SECOND %-8.1f", frames_per_sec); + strncpy(p_str, temp_str, strlen(temp_str)); } /* Create Target Filename */ @@ -1101,7 +1100,7 @@ static TOOL_ERROR Output_Wmops_File( char *PathName, float frames_per_sec, bool } /* Write the whole content to the target file */ - fwrite(text, size, 1, TargetFile); + fwrite(text, strlen(text), sizeof(char), TargetFile); fclose(TargetFile); free(text); } From 367cf4b94bfec2dc318777b1ca3222acf8630c5c Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 19 Sep 2023 15:26:47 +0200 Subject: [PATCH 21/53] introduce dynamic allocation of internal WMOPS buffers and lists (repplacing constants MAX_RECORDS and MAX_RECORDABLE_CALLS), allows for re-allocation if max size is exceeded --- src/wmc_tool/test_data/ref/main1.c | 2 + src/wmc_tool/test_data/ref/test_basop.c | 2 + src/wmc_tool/test_data/ref/test_basop32.c | 1 + src/wmc_tool/test_data/ref/wmc_auto.c | 3905 +++++++++++---------- src/wmc_tool/test_data/ref/wmc_auto.h | Bin 43846 -> 42697 bytes src/wmc_tool/test_data/src/main1.c | 2 + src/wmc_tool/test_data/src/test_basop.c | 2 + src/wmc_tool/test_data/src/test_basop32.c | 1 + src/wmc_tool/wmc_auto_c.txt | 243 +- src/wmc_tool/wmc_auto_h.txt | 9 +- 10 files changed, 2139 insertions(+), 2028 deletions(-) diff --git a/src/wmc_tool/test_data/ref/main1.c b/src/wmc_tool/test_data/ref/main1.c index e7753d18..45657cd3 100644 --- a/src/wmc_tool/test_data/ref/main1.c +++ b/src/wmc_tool/test_data/ref/main1.c @@ -65,3 +65,5 @@ int main( #endif return 0; } + +#undef WMC_TOOL_SKIP \ No newline at end of file diff --git a/src/wmc_tool/test_data/ref/test_basop.c b/src/wmc_tool/test_data/ref/test_basop.c index a3eef180..b090bf13 100644 --- a/src/wmc_tool/test_data/ref/test_basop.c +++ b/src/wmc_tool/test_data/ref/test_basop.c @@ -53,3 +53,5 @@ void weight_coef_a( const Word16 *a, Word16 *b, const Word16 ght ) return; } + +#undef WMC_TOOL_SKIP diff --git a/src/wmc_tool/test_data/ref/test_basop32.c b/src/wmc_tool/test_data/ref/test_basop32.c index 8a6875da..e0e733a2 100644 --- a/src/wmc_tool/test_data/ref/test_basop32.c +++ b/src/wmc_tool/test_data/ref/test_basop32.c @@ -2427,5 +2427,6 @@ Word32 L_msu0 (Word32 L_var3, Word16 var1, Word16 var2) { return (L_var_out); } +#undef WMC_TOOL_SKIP /* end of file */ diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index 955c7cab..8e70ebe2 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -1,1927 +1,1978 @@ -/* - * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. - * - * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, - * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file - * or refer to ITU-T Recommendation G.191 on "SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS". - * - * Any use of this software is permitted provided that this notice is not removed and that neither the authors nor - * VoiceAge Corporation are deemed to have made any representations as to the suitability of this software - * for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE. - * - * Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca) - */ - -#include -#include -#include -#include -#include - -#ifndef _MSC_VER -#include -#include -#else -#include -#endif - -#include "wmc_auto.h" - - -#define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */ - -#ifdef WMOPS - -/*-------------------------------------------------------------------* - * Complexity counting tool - *--------------------------------------------------------------------*/ - -#define MAX_RECORDS 1024 -#define MAX_CHAR 64 -#define MAX_STACK 64 -#define DOUBLE_MAX 0x80000000 - -struct wmops_record -{ - char label[MAX_CHAR]; - long call_number; - long update_cnt; - int call_tree[MAX_RECORDS]; - double start_selfcnt; - double current_selfcnt; - double max_selfcnt; - double min_selfcnt; - double tot_selfcnt; - double start_cnt; /* The following take into account the decendants */ - double current_cnt; - double max_cnt; - double min_cnt; - double tot_cnt; -#ifdef WMOPS_WC_FRAME_ANALYSIS - int32_t current_call_number; - double wc_cnt; - double wc_selfcnt; - int32_t wc_call_number; -#endif -}; - -double ops_cnt; -double prom_cnt; -double inst_cnt[NUM_INST]; - -static struct wmops_record wmops[MAX_RECORDS]; -static int stack[MAX_STACK]; -static int sptr; -static int num_records; -static int current_record; -static long update_cnt; -static double start_cnt; -static double max_cnt; -static double min_cnt; -static double inst_cnt_wc[NUM_INST]; -static long fnum_cnt_wc; - -static int *heap_allocation_call_tree = NULL, heap_allocation_call_tree_size = 0, heap_allocation_call_tree_max_size = 0; - - -void reset_wmops( void ) -{ - int i, j; - - for ( i = 0; i < MAX_RECORDS; i++ ) - { - strcpy( &wmops[i].label[0], "\0" ); - wmops[i].call_number = 0; - wmops[i].update_cnt = 0; - for ( j = 0; j < MAX_RECORDS; j++ ) - { - wmops[i].call_tree[j] = -1; - } - wmops[i].start_selfcnt = 0.0; - wmops[i].current_selfcnt = 0.0; - wmops[i].max_selfcnt = 0.0; - wmops[i].min_selfcnt = DOUBLE_MAX; - wmops[i].tot_selfcnt = 0.0; - wmops[i].start_cnt = 0.0; - wmops[i].current_cnt = 0.0; - wmops[i].max_cnt = 0.0; - wmops[i].min_cnt = DOUBLE_MAX; - wmops[i].tot_cnt = 0.0; -#ifdef WMOPS_WC_FRAME_ANALYSIS - wmops[i].wc_cnt = 0.0; - wmops[i].wc_selfcnt = 0.0; - wmops[i].current_call_number = 0; -#endif - } - - for ( i = 0; i < MAX_STACK; i++ ) - { - stack[i] = -1; - } - sptr = 0; - num_records = 0; - current_record = -1; - update_cnt = 0; - - max_cnt = 0.0; - min_cnt = DOUBLE_MAX; - start_cnt = 0.0; - ops_cnt = 0.0; -} - - -void push_wmops( const char *label ) -{ - int new_flag; - int i, j; - - /* Check if new function record label */ - new_flag = 1; - for ( i = 0; i < num_records; i++ ) - { - if ( strcmp( wmops[i].label, label ) == 0 ) - { - new_flag = 0; - break; - } - } - - /* Configure new record */ - if ( new_flag ) - { - if ( num_records >= MAX_RECORDS ) - { - fprintf( stdout, "push_wmops(): exceeded MAX_RECORDS count.\n\n" ); - exit( -1 ); - } - strcpy( wmops[i].label, label ); - num_records++; - } - - /* Push current context onto stack */ - if ( current_record >= 0 ) - { - if ( sptr >= MAX_STACK ) - { - fprintf( stdout, "\r push_wmops(): stack exceeded, try inreasing MAX_STACK\n" ); - exit( -1 ); - } - stack[sptr++] = current_record; - - /* accumulate op counts */ - wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; - - /* update call tree */ - for ( j = 0; j < MAX_RECORDS; j++ ) - { - if ( wmops[i].call_tree[j] == current_record ) - { - break; - } - else if ( wmops[i].call_tree[j] == -1 ) - { - wmops[i].call_tree[j] = current_record; - break; - } - } - } - - /* init current record */ - current_record = i; - wmops[current_record].start_selfcnt = ops_cnt; - wmops[current_record].start_cnt = ops_cnt; - wmops[current_record].call_number++; -#ifdef WMOPS_WC_FRAME_ANALYSIS - wmops[current_record].current_call_number++; -#endif - - return; -} - - -void pop_wmops( void ) -{ - - /* Check for underflow */ - if ( current_record < 0 ) - { - fprintf( stdout, "\r pop_wmops(): stack underflow, too many calls to pop_wmops()\n" ); - exit( -1 ); - } - - /* update count of current record */ - wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; - wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt; - - /* Get back previous context from stack */ - if ( sptr > 0 ) - { - current_record = stack[--sptr]; - wmops[current_record].start_selfcnt = ops_cnt; - } - else - { - current_record = -1; - } - - return; -} - - -void update_wmops( void ) -{ - int i; - double current_cnt; -#ifdef WMOPS_PER_FRAME - static FILE *fid = NULL; - const char filename[] = "wmops_analysis"; - float tmpF; -#endif - - if ( sptr != 0 ) - { - fprintf( stdout, "update_wmops(): Stack must be empty!\n" ); - exit( -1 ); - } - -#ifdef WMOPS_PER_FRAME - /* Check, if the output file has already been opened */ - if ( fid == NULL ) - { - fid = fopen( filename, "wb" ); - - if ( fid == NULL ) - { - fprintf( stderr, "\nCannot open %s!\n\n", filename ); - exit( -1 ); - } - } - - /* Write current complexity to the external file */ - tmpF = (float) ( FAC * wmops[0].current_cnt ); - fwrite( &tmpF, sizeof( float ), 1, fid ); -#endif - -#ifdef WMOPS_WC_FRAME_ANALYSIS - if ( ops_cnt - start_cnt > max_cnt ) - { - for ( i = 0; i < num_records; i++ ) - { - wmops[i].wc_cnt = wmops[i].current_cnt; - wmops[i].wc_selfcnt = wmops[i].current_selfcnt; - wmops[i].wc_call_number = wmops[i].current_call_number; - } - } -#endif - - for ( i = 0; i < num_records; i++ ) - { - wmops[i].tot_selfcnt += wmops[i].current_selfcnt; - wmops[i].tot_cnt += wmops[i].current_cnt; - - if ( wmops[i].current_selfcnt > 0 ) - { - if ( wmops[i].current_selfcnt > wmops[i].max_selfcnt ) - { - wmops[i].max_selfcnt = wmops[i].current_selfcnt; - } - - if ( wmops[i].current_selfcnt < wmops[i].min_selfcnt ) - { - wmops[i].min_selfcnt = wmops[i].current_selfcnt; - } - } - - wmops[i].current_selfcnt = 0; - - if ( wmops[i].current_cnt > 0 ) - { - if ( wmops[i].current_cnt > wmops[i].max_cnt ) - { - wmops[i].max_cnt = wmops[i].current_cnt; - } - - if ( wmops[i].current_cnt < wmops[i].min_cnt ) - { - wmops[i].min_cnt = wmops[i].current_cnt; - } - - wmops[i].update_cnt++; - } - - wmops[i].current_cnt = 0; -#ifdef WMOPS_WC_FRAME_ANALYSIS - wmops[i].current_call_number = 0; -#endif - } - - current_cnt = ops_cnt - start_cnt; - if ( current_cnt > max_cnt ) - { - max_cnt = current_cnt; - - for ( i = 0; i < NUM_INST; i++ ) - { - inst_cnt_wc[i] = inst_cnt[i]; - } - - fnum_cnt_wc = update_cnt + 1; - } - - if ( current_cnt < min_cnt ) - { - min_cnt = current_cnt; - } - - for ( i = 0; i < NUM_INST; i++ ) - { - inst_cnt[i] = 0.0; - } - - start_cnt = ops_cnt; - - /* increment frame counter */ - update_cnt++; - - return; -} - - -void print_wmops( void ) -{ - int i, label_len, max_label_len; - - char *sfmts = "%*s %8s %8s %7s %7s\n"; - char *dfmts = "%*s %8.2f %8.3f %7.3f %7.3f\n"; - char *sfmt = "%*s %8s %8s %7s %7s %7s %7s %7s\n"; - char *dfmt = "%*s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\n"; - -#ifdef WMOPS_WC_FRAME_ANALYSIS - int j; - char *sfmtt = "%20s %4s %15s\n"; - char *dfmtt = "%20s %4d "; -#endif - - /* calculate maximum label length for compact prinout */ - max_label_len = 0; - for ( i = 0; i < num_records; i++ ) - { - label_len = strlen( wmops[i].label ); - if ( label_len > max_label_len ) - { - max_label_len = label_len; - } - } - max_label_len += 4; - - fprintf( stdout, "\n\n --- Complexity analysis [WMOPS] --- \n\n" ); - - fprintf( stdout, "%*s %33s %23s\n", max_label_len, "", "|------ SELF ------|", "|--- CUMULATIVE ---|" ); - fprintf( stdout, sfmt, max_label_len, " routine", " calls", " min ", " max ", " avg ", " min ", " max ", " avg " ); - fprintf( stdout, sfmt, max_label_len, "---------------", "------", "------", "------", "------", "------", "------", "------" ); - - for ( i = 0; i < num_records; i++ ) - { - fprintf( stdout, dfmt, max_label_len, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt, - wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt, - FAC * wmops[i].max_selfcnt, - wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_selfcnt / wmops[i].update_cnt, - wmops[i].min_cnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_cnt, - FAC * wmops[i].max_cnt, - wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_cnt / wmops[i].update_cnt ); - } - - fprintf( stdout, sfmts, max_label_len, "---------------", "------", "------", "------", "------" ); - fprintf( stdout, dfmts, max_label_len, "total", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt ); - fprintf( stdout, "\n" ); - -#ifdef WMOPS_WC_FRAME_ANALYSIS - fprintf( stdout, "\nComplexity analysis for the worst-case frame %ld:\n", fnum_cnt_wc ); - fprintf( stdout, "%*s %8s %10s %12s\n", max_label_len, " routine", " calls", " SELF", " CUMULATIVE" ); - fprintf( stdout, "%*s %8s %10s %10s\n", max_label_len, "---------------", "------", "------", "----------" ); - - for ( i = 0; i < num_records; i++ ) - { - fprintf( stdout, "%*s %8d %10.3f %12.3f\n", max_label_len, wmops[i].label, wmops[i].wc_call_number, FAC * wmops[i].wc_selfcnt, FAC * wmops[i].wc_cnt ); - } - - fprintf( stdout, "\nCall Tree:\n\n" ); - fprintf( stdout, sfmtt, " function", "num", "called by: " ); - fprintf( stdout, sfmtt, "---------------", "---", "--------------" ); - - for ( i = 0; i < num_records; i++ ) - { - fprintf( stdout, dfmtt, wmops[i].label, i ); - for ( j = 0; wmops[i].call_tree[j] != -1; j++ ) - { - if ( j != 0 ) - { - fprintf( stdout, ", " ); - } - fprintf( stdout, "%d", wmops[i].call_tree[j] ); - } - fprintf( stdout, "\n" ); - } - - fprintf( stdout, sfmtt, "---------------", "---", "--------------" ); - fprintf( stdout, "\n\n" ); - - fprintf( stdout, "\nInstruction type analysis for the worst-case frame %ld:\n\n", fnum_cnt_wc ); /* added -- JPA */ - for ( i = 0; i < NUM_INST; i++ ) - { - switch ( (enum instructions) i ) - { - case _ADD: - fprintf( stdout, "\tAdds: %12.1f\n", inst_cnt_wc[i] ); - break; - case _ABS: - fprintf( stdout, "\tAbsolutes: %12.1f\n", inst_cnt_wc[i] ); - break; - case _MULT: - fprintf( stdout, "\tMultiplies: %12.1f\n", inst_cnt_wc[i] ); - break; - case _MAC: - fprintf( stdout, "\tMACs: %12.1f\n", inst_cnt_wc[i] ); - break; - case _MOVE: - fprintf( stdout, "\tMoves: %12.1f\n", inst_cnt_wc[i] ); - break; - case _STORE: - fprintf( stdout, "\tStores: %12.1f\n", inst_cnt_wc[i] ); - break; - case _LOGIC: - fprintf( stdout, "\tLogicals: %12.1f\n", inst_cnt_wc[i] ); - break; - case _SHIFT: - fprintf( stdout, "\tShifts: %12.1f\n", inst_cnt_wc[i] ); - break; - case _BRANCH: - fprintf( stdout, "\tBranches: %12.1f\n", inst_cnt_wc[i] ); - break; - case _DIV: - fprintf( stdout, "\tDivisions: %12.1f\n", inst_cnt_wc[i] ); - break; - case _SQRT: - fprintf( stdout, "\tSquare Root: %12.1f\n", inst_cnt_wc[i] ); - break; - case _TRANS: - fprintf( stdout, "\tTrans: %12.1f\n", inst_cnt_wc[i] ); - break; - case _FUNC: - fprintf( stdout, "\tFunc Call: %12.1f\n", inst_cnt_wc[i] ); - break; - case _LOOP: - fprintf( stdout, "\tLoop Init: %12.1f\n", inst_cnt_wc[i] ); - break; - case _INDIRECT: - fprintf( stdout, "\tIndirect Addr: %12.1f\n", inst_cnt_wc[i] ); - break; - case _PTR_INIT: - fprintf( stdout, "\tPointer Init: %12.1f\n", inst_cnt_wc[i] ); - break; - case _TEST: - fprintf( stdout, "\tExtra condit.: %12.1f\n", inst_cnt_wc[i] ); - break; - case _POWER: - fprintf( stdout, "\tExponential: %12.1f\n", inst_cnt_wc[i] ); - break; - case _LOG: - fprintf( stdout, "\tLogarithm: %12.1f\n", inst_cnt_wc[i] ); - break; - case _MISC: - fprintf( stdout, "\tAll other op.: %12.1f\n", inst_cnt_wc[i] ); - break; - default: - fprintf( stdout, "\tERROR: Invalid instruction type: %d\n\n", i ); - } - } -#endif - - return; -} - - -/*-------------------------------------------------------------------* - * Memory counting tool measuring RAM usage (stack and heap) - * - * Maximum RAM is measured by monitoring the total allocated memory (stack and heap) in each frame. - * - * Maximum stack is measured by monitoring the difference between the 'top' and 'bottom' of the stack. The 'bottom' of the stack is updated in each function - * with a macro 'func_start_' which is inserted automatically to all functions during the instrumentation process. - * - * Maximum heap is measured by summing the sizes of all memory blocks allocated by malloc() or calloc() and deallocated by free(). The maximum heap size is - * updated each time when the macros malloc_() or calloc_() is invoked. The macros 'malloc_ and calloc_' are inserted automatically during the instrumentation process. - * As part of heap measurements, intra-frame heap and inter-frame heap are measured separately. Intra-frame heap refers to heap memory which is allocated and deallocated - * within a single frame. Inter-frame heap, on the contrary, refers to heap memory which is reserved for more than one frame. - * - * In order to run the memory counting tool the function reset_mem(cnt_size) must be called at the beginning of the encoding/decoding process. - * The unit in which memory consumption is reported is set via the parameter 'cnt_size'. It can be set to 0 (bytes), 1 (32b words) or 2 (64b words). - * At the end of the encoding/decoding process, 'print_mem()' function may be called to print basic information about memory consumption. If the macro 'MEM_COUNT_DETAILS' - * is activated, detailed information is printed - * - * The macro 'WMOPS' needs to be activated to enable memory counting. To avoid the instrumentation of malloc()/calloc()/free() calls, use - * #define WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP macro pair around the malloc(), calloc() and free(). - *--------------------------------------------------------------------*/ - -#define MAX_RECORDABLE_CALLS 100 -#define MAX_FUNCTION_NAME_LENGTH 35 /* Maximum length that the function string will be truncated to */ -#define MAX_PARAMS_LENGTH 50 /* Maximum length that the parameter string will be truncated to */ -#define MAX_NUM_RECORDS 300 /* Initial maximum number of memory records -> mightb be increased during runtime, if needed */ -#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of memory records, increase the number of records by this number */ - -/* This is the value (in bytes) towards which the block size is rounded. For example, a block of 123 bytes, when using - a 32 bits system, will end up taking 124 bytes since the last unused byte cannot be used for another block. */ -#ifdef MEM_ALIGN_64BITS -#define BLOCK_ROUNDING 8 /* Align on 64 Bits */ -#else -#define BLOCK_ROUNDING 4 /* Align on 32 Bits */ -#endif - -#define N_32BITS_BLOCKS ( BLOCK_ROUNDING / sizeof( int32_t ) ) - -#define MAGIC_VALUE_OOB 0x12A534F0 /* Signature value which is inserted before and after each allocated memory block, used to detect out-of-bound access */ -#define MAGIC_VALUE_USED ( ~MAGIC_VALUE_OOB ) /* Value used to pre-fill allocated memory blocks, used to calculate actual memory usage */ -#define OOB_START 0x1 /* Flag indicating out-of-bounds access before memory block */ -#define OOB_END 0x2 /* Flag indicating out-of-bounds access after memory block */ - -#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) ) -#define IS_CALLOC( str ) ( str[0] == 'c' ) - -#ifdef MEM_COUNT_DETAILS -const char *csv_filename = "mem_analysis.csv"; -static FILE *fid_csv_filename = NULL; -#endif - -typedef struct -{ - char function_name[MAX_FUNCTION_NAME_LENGTH + 1]; - int16_t *stack_ptr; -} caller_info; - -caller_info stack_callers[2][MAX_RECORDABLE_CALLS]; - -typedef struct -{ - char name[MAX_FUNCTION_NAME_LENGTH + 1]; /* +1 for NUL */ - char params[1 + MAX_PARAMS_LENGTH + 1]; /* +1 for 'm'/'c' alloc & +1 for NUL */ - unsigned long hash; - int lineno; - void *block_ptr; - int block_size; - unsigned long total_block_size; /* Cumulative sum of the allocated size in the session */ - unsigned long total_used_size; /* Cumulative sum of the used size in the session */ - int wc_heap_size_intra_frame; /* Worst-Case Intra-Frame Heap Size */ - int wc_heap_size_inter_frame; /* Worst-Case Inter-Frame Heap Size */ - int frame_allocated; /* Frame number in which the Memory Block has been allocated (-1 if not allocated at the moment) */ - int OOB_Flag; - int noccurances; /* Number of times that the memory block has been allocated in a frame */ -} allocator_record; - -allocator_record *allocation_list = NULL; - -static int16_t *ptr_base_stack = 0; /* Pointer to the bottom of stack (base pointer). Stack grows up. */ -static int16_t *ptr_current_stack = 0; /* Pointer to the current stack pointer */ -static int16_t *ptr_max_stack = 0; /* Pointer to the maximum stack pointer (the farest point from the bottom of stack) */ -static int32_t wc_stack_frame = 0; /* Frame corresponding to the worst-case stack usage */ -static int32_t wc_ram_size, wc_ram_frame; -static int32_t current_heap_size; -static int current_calls = 0; -static char location_max_stack[256] = "undefined"; -static int Num_Records, Max_Num_Records; -static size_t Stat_Cnt_Size = USE_BYTES; -static const char *Count_Unit[] = { "bytes", "words", "words" }; - -static int *list_wc_intra_frame_heap, n_items_wc_intra_frame_heap, max_items_wc_intra_frame_heap, size_wc_intra_frame_heap, location_wc_intra_frame_heap; -static int *list_current_inter_frame_heap, n_items_current_inter_frame_heap, max_items_current_inter_frame_heap, size_current_inter_frame_heap; -static int *list_wc_inter_frame_heap, n_items_wc_inter_frame_heap, max_items_wc_inter_frame_heap, size_wc_inter_frame_heap, location_wc_inter_frame_heap; - -/* Local Functions */ -static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str ); -allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record ); -static void *mem_alloc_block( size_t size, const char *size_str ); - -/*-------------------------------------------------------------------* - * reset_mem() - * - * Initialize/reset memory counting tool (stack and heap) - *--------------------------------------------------------------------*/ - -void reset_mem( Counting_Size cnt_size ) -{ - int16_t something; - size_t tmp_size; - - /* initialize stack pointers */ - ptr_base_stack = &something; - ptr_max_stack = ptr_base_stack; - ptr_current_stack = ptr_base_stack; - - Stat_Cnt_Size = cnt_size; - - /* Check, if sizeof(int32_t) is 4 bytes */ - tmp_size = sizeof( int32_t ); - if ( tmp_size != 4 ) - { - fprintf( stderr, "Error: Expecting 'int32_t' to be a 32 Bits Integer!" ); - exit( -1 ); - } - - /* create allocation list for malloc() memory blocks */ - if ( allocation_list == NULL ) - { - allocation_list = malloc( MAX_NUM_RECORDS * sizeof( allocator_record ) ); - } - - if ( allocation_list == NULL ) - { - fprintf( stderr, "Error: Unable to Create List of Memory Blocks!" ); - exit( -1 ); - } - - Num_Records = 0; - Max_Num_Records = MAX_NUM_RECORDS; - - wc_ram_size = 0; - wc_ram_frame = -1; - current_heap_size = 0; - - /* heap allocation tree */ - heap_allocation_call_tree_max_size = MAX_NUM_RECORDS; - if ( heap_allocation_call_tree == NULL ) - { - heap_allocation_call_tree = (int *) malloc( heap_allocation_call_tree_max_size * sizeof( int ) ); - memset( heap_allocation_call_tree, -1, heap_allocation_call_tree_max_size * sizeof( int ) ); - } - heap_allocation_call_tree_size = 0; - - /* wc intra-frame heap */ - max_items_wc_intra_frame_heap = MAX_NUM_RECORDS; - if ( list_wc_intra_frame_heap == NULL ) - { - list_wc_intra_frame_heap = (int *) malloc( max_items_wc_intra_frame_heap * sizeof( int ) ); - memset( list_wc_intra_frame_heap, -1, max_items_wc_intra_frame_heap * sizeof( int ) ); - } - n_items_wc_intra_frame_heap = 0; - size_wc_intra_frame_heap = 0; - location_wc_intra_frame_heap = -1; - - /* current inter-frame heap */ - max_items_current_inter_frame_heap = MAX_NUM_RECORDS; - if ( list_current_inter_frame_heap == NULL ) - { - list_current_inter_frame_heap = (int *) malloc( max_items_current_inter_frame_heap * sizeof( int ) ); - memset( list_current_inter_frame_heap, -1, max_items_current_inter_frame_heap * sizeof( int ) ); - } - n_items_current_inter_frame_heap = 0; - size_current_inter_frame_heap = 0; - - /* wc inter-frame heap */ - max_items_wc_inter_frame_heap = MAX_NUM_RECORDS; - if ( list_wc_inter_frame_heap == NULL ) - { - list_wc_inter_frame_heap = (int *) malloc( max_items_wc_inter_frame_heap * sizeof( int ) ); - memset( list_wc_inter_frame_heap, -1, max_items_wc_inter_frame_heap * sizeof( int ) ); - } - n_items_wc_inter_frame_heap = 0; - size_wc_inter_frame_heap = 0; - location_wc_inter_frame_heap = -1; - -#ifdef MEM_COUNT_DETAILS - /* Check, if the .csv file has already been opened */ - if ( fid_csv_filename == NULL ) - { - fid_csv_filename = fopen( csv_filename, "wb" ); - - if ( fid_csv_filename == NULL ) - { - fprintf( stderr, "\nCannot open %s!\n\n", csv_filename ); - exit( -1 ); - } - } - else - { - /* reset file */ - rewind( fid_csv_filename ); - } -#endif - - return; -} - -/*-------------------------------------------------------------------* - * reset_stack() - * - * Reset stack pointer - *--------------------------------------------------------------------*/ - -void reset_stack( void ) -{ - int16_t something; - - /* initialize/reset stack pointers */ - ptr_base_stack = &something; - ptr_max_stack = ptr_base_stack; - ptr_current_stack = ptr_base_stack; - - return; -} - -/*-------------------------------------------------------------------* - * push_stack() - * - * Check the current stack pointer and update the maximum stack pointer, if new maximum found. - *--------------------------------------------------------------------*/ - -int push_stack( const char *filename, const char *fctname ) -{ - int16_t something; - int32_t current_stack_size; - - ptr_current_stack = &something; - - (void) *filename; /* to avoid compilation warning */ - - /* Is there room to save the caller's information? */ - if ( current_calls >= MAX_RECORDABLE_CALLS ) - { /* No */ - fprintf( stderr, "No more room to store call stack info. Please increase MAX_RECORDABLE_CALLS" ); - exit( -1 ); - } - - /* Valid Function Name? */ - if ( fctname[0] == 0 ) - { /* No */ - fprintf( stderr, "Invalid function name for call stack info." ); - exit( -1 ); - } - - /* Save the Name of the Calling Function in the Table */ - strncpy( stack_callers[0][current_calls].function_name, fctname, MAX_FUNCTION_NAME_LENGTH ); - stack_callers[0][current_calls].function_name[MAX_FUNCTION_NAME_LENGTH] = 0; /* Nul Terminate */ - - /* Save the Stack Pointer */ - stack_callers[0][current_calls].stack_ptr = ptr_current_stack; - - /* Increase Stack Calling Tree Level */ - current_calls++; - - /* Is this the First Time or the Worst Case? */ - if ( ptr_current_stack < ptr_max_stack || ptr_max_stack == NULL ) - { /* Yes */ - /* Save Info about it */ - ptr_max_stack = ptr_current_stack; - - wc_stack_frame = update_cnt; /* current frame number is stored in the variable update_cnt and updated in the function update_wmops() */ - strncpy( location_max_stack, fctname, sizeof( location_max_stack ) - 1 ); - location_max_stack[sizeof( location_max_stack ) - 1] = '\0'; - - /* Save Call Tree */ - memmove( stack_callers[1], stack_callers[0], sizeof( caller_info ) * current_calls ); - - /* Terminate the List (Unless Full) */ - if ( current_calls < MAX_RECORDABLE_CALLS ) - { - stack_callers[1][current_calls].function_name[0] = 0; - } - } - - /* Check, if This is the New Worst-Case RAM (stack + heap) */ - current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) ); - - if ( current_stack_size < 0 ) - { - /* prevent negative stack size */ - current_stack_size = 0; - } - - if ( current_stack_size + current_heap_size > wc_ram_size ) - { - wc_ram_size = current_stack_size + current_heap_size; - wc_ram_frame = update_cnt; - } - - return 0 /* for Now */; -} - -/*-------------------------------------------------------------------* - * pop_stack() - * - * Remove stack caller entry from the list - *--------------------------------------------------------------------*/ - -int pop_stack( const char *filename, const char *fctname ) -{ - caller_info *caller_info_ptr; - - (void) *filename; /* to avoid compilation warning */ - - /* Decrease Stack Calling */ - current_calls--; - - /* Get Pointer to Caller Information */ - caller_info_ptr = &stack_callers[0][current_calls]; - - /* Check, if Names Match */ - if ( strncmp( caller_info_ptr->function_name, fctname, MAX_FUNCTION_NAME_LENGTH ) != 0 ) - { - fprintf( stderr, "Invalid usage of pop_stack()" ); - exit( -1 ); - } - - /* Erase Entry */ - caller_info_ptr->function_name[0] = 0; - - /* Retrieve previous stack pointer */ - if ( current_calls == 0 ) - { - ptr_current_stack = ptr_base_stack; - } - else - { - ptr_current_stack = stack_callers[0][current_calls - 1].stack_ptr; - } - - return 0 /* for Now */; -} - -#ifdef MEM_COUNT_DETAILS -/*-------------------------------------------------------------------* - * print_stack_call_tree() - * - * Print detailed information about worst-case stack usage - *--------------------------------------------------------------------*/ - -static void print_stack_call_tree( void ) -{ - caller_info *caller_info_ptr; - int call_level; - char fctname[MAX_FUNCTION_NAME_LENGTH + 1]; - - fprintf( stdout, "\nList of functions when maximum stack size is reached:\n\n" ); - - caller_info_ptr = &stack_callers[1][0]; - for ( call_level = 0; call_level < MAX_RECORDABLE_CALLS; call_level++ ) - { - /* Done? */ - if ( caller_info_ptr->function_name[0] == 0 ) - { - break; - } - - /* Print Name */ - strncpy( fctname, caller_info_ptr->function_name, MAX_FUNCTION_NAME_LENGTH ); - strcat( fctname, "()" ); - fprintf( stdout, "%-42s", fctname ); - - /* Print Stack Usage (Based on Difference) */ - if ( call_level != 0 ) - { - fprintf( stdout, "%lu %s\n", ( ( ( caller_info_ptr - 1 )->stack_ptr - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); - } - else - { - fprintf( stdout, "%lu %s\n", ( ( ptr_base_stack - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); - } - - /* Advance */ - caller_info_ptr++; - } - - fprintf( stdout, "\n" ); - - return; -} -#endif - - -/*-------------------------------------------------------------------* - * mem_alloc() - * - * Creates new record, stores auxiliary information about which function allocated the memory, line number, parameters, etc. - * Finally, it allocates physical memory using malloc() - * The function also updates worst-case heap size and worst-case RAM size - *--------------------------------------------------------------------*/ - -void *mem_alloc( - const char *func_name, - int func_lineno, - size_t size, - char *size_str /* the first char indicates m-alloc or c-alloc */ ) -{ - int index_record; - int32_t current_stack_size; - unsigned long hash; - allocator_record *ptr_record; - - if ( size == 0 ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Size of Zero not Supported" ); - exit( -1 ); - } - - /* Search for an existing record (that has been de-allocated before) */ - index_record = 0; - while ( ( ptr_record = get_mem_record( &hash, func_name, func_lineno, size_str, &index_record ) ) != NULL ) - { - if ( ptr_record->frame_allocated == -1 ) - { - break; - } - else - { - index_record++; - } - } - - /* Create new record */ - if ( ptr_record == NULL ) - { - if ( Num_Records >= Max_Num_Records ) - { - /* There is no room for a new record -> reallocate memory */ - Max_Num_Records += MAX_NUM_RECORDS_REALLOC_STEP; - allocation_list = realloc( allocation_list, Max_Num_Records * sizeof( allocator_record ) ); - } - - ptr_record = &( allocation_list[Num_Records] ); - - /* Initialize new record */ - ptr_record->hash = hash; - ptr_record->noccurances = 0; - ptr_record->total_block_size = 0; - ptr_record->total_used_size = 0; - ptr_record->frame_allocated = -1; - ptr_record->OOB_Flag = 0; - ptr_record->wc_heap_size_intra_frame = -1; - ptr_record->wc_heap_size_inter_frame = -1; - - index_record = Num_Records; - Num_Records++; - } - - /* Allocate memory block for the new record, add signature before the beginning and after the memory block and fill it with magic value */ - ptr_record->block_ptr = mem_alloc_block( size, size_str ); - - if ( ptr_record->block_ptr == NULL ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Cannot Allocate Memory!" ); - exit( -1 ); - } - - /* Save all auxiliary information about the memory block */ - strncpy( ptr_record->name, func_name, MAX_FUNCTION_NAME_LENGTH ); - ptr_record->name[MAX_FUNCTION_NAME_LENGTH] = '\0'; - strncpy( ptr_record->params, size_str, MAX_PARAMS_LENGTH ); /* Note: The size string starts with either 'm' or 'c' to indicate 'm'alloc or 'c'alloc */ - ptr_record->params[MAX_PARAMS_LENGTH] = '\0'; - ptr_record->lineno = func_lineno; - ptr_record->block_size = size; - ptr_record->total_block_size += size; - -#ifdef MEM_COUNT_DETAILS - /* Export heap memory allocation record to the .csv file */ - fprintf( fid_csv_filename, "A,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); -#endif - - if ( ptr_record->frame_allocated != -1 ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Attempt to Allocate the Same Memory Block with Freeing it First!" ); - exit( -1 ); - } - - ptr_record->frame_allocated = update_cnt; /* Store the current frame number -> later it will be used to determine the total duration */ - - /* Update Heap Size in the current frame */ - current_heap_size += ptr_record->block_size; - - /* Check, if this is the new Worst-Case RAM (stack + heap) */ - current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) ); - if ( current_stack_size + current_heap_size > wc_ram_size ) - { - wc_ram_size = current_stack_size + current_heap_size; - wc_ram_frame = update_cnt; - } - - /* Add new entry to the heap allocation call tree */ - if ( heap_allocation_call_tree == NULL ) - { - fprintf( stderr, "Error: Heap allocation call tree not created!" ); - exit( -1 ); - } - - /* check, if the maximum size of the call tree has been reached -> resize if so */ - if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size ) - { - heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP; - heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) ); - } - - /* push new entry (positive number means push op, neagtive number means pop op; zero index must be converted to 0.01 :-) */ - heap_allocation_call_tree[heap_allocation_call_tree_size++] = index_record; - - return ptr_record->block_ptr; -} - -/*-------------------------------------------------------------------* - * mem_alloc_block() - * - * Physical allocation of memory using malloc(). Appends 'signature' before and after the block, - * pre-fills memory block with magic value - *--------------------------------------------------------------------*/ - -static void *mem_alloc_block( size_t size, const char *size_str ) -{ - size_t rounded_size; - void *block_ptr; - char *tmp_ptr; - size_t n, f; - int32_t fill_value; - int32_t *ptr32; - int32_t mask, temp; - - /* Round Up Block Size */ - rounded_size = ROUND_BLOCK_SIZE( size ); - - /* Allocate memory using the standard malloc() by adding room for Signature Values */ - block_ptr = malloc( rounded_size + BLOCK_ROUNDING * 2 ); - - if ( block_ptr == NULL ) - { - return NULL; - } - - /* Add Signature Before the Start of the Block */ - ptr32 = (int32_t *) block_ptr; - n = N_32BITS_BLOCKS; - do - { - *ptr32++ = MAGIC_VALUE_OOB; - } while ( --n ); - - /* Fill Memory Block with Magic Value or 0 */ - fill_value = MAGIC_VALUE_USED; - if ( IS_CALLOC( size_str ) ) - { - fill_value = 0x00000000; - } - n = size / sizeof( int32_t ); - while ( n-- ) - { - *ptr32++ = fill_value; - } - - /* Fill the Reminder of the Memory Block - After Rounding */ - n = rounded_size - size; - f = n % sizeof( int32_t ); - if ( f != 0 ) - { - /* when filling with '0' need to adapt the magic value */ - /* shift by [1->24, 2->16, 3->8] */ - mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); /* (1) */ - temp = MAGIC_VALUE_OOB & mask; - if ( fill_value != 0x0 ) - { /* for malloc merge fill value */ - temp += ( ~mask ) & MAGIC_VALUE_USED; - } /* for calloc the code in (1) above already introduces zeros */ - *ptr32++ = temp; - } - n /= sizeof( int32_t ); - n += N_32BITS_BLOCKS; - - /* Add Signature After the End of Block */ - do - { - *ptr32++ = MAGIC_VALUE_OOB; - } while ( --n ); - - /* Adjust the Memory Block Pointer (Magic Value Before and After the Memory Block Requested) */ - tmp_ptr = (char *) block_ptr; - tmp_ptr += BLOCK_ROUNDING; - block_ptr = (void *) tmp_ptr; - - return block_ptr; -} - -/*-------------------------------------------------------------------* - * mem_set_usage() - * - * Calculates actual usage of memory block by checking the magic value that was used to pre-fill - * each memory block during its allocation - *--------------------------------------------------------------------*/ - -static int mem_set_usage( allocator_record *record_ptr ) -{ - int total_bytes_used; - - size_t n; - int32_t *ptr32; - char *ptr8; - size_t total_bytes; - int32_t fill_value; - - fill_value = MAGIC_VALUE_USED; - if ( ( record_ptr->params[0] ) == 'c' ) - { - fill_value = 0x00000000; - } - - total_bytes = record_ptr->block_size; - - /* Check 4 bytes at a time */ - ptr32 = (int32_t *) record_ptr->block_ptr; - total_bytes_used = 0; - for ( n = total_bytes / sizeof( int32_t ); n > 0; n-- ) - { - if ( *ptr32++ != fill_value ) - { - total_bytes_used += sizeof( int32_t ); - } - } - - /* Check remaining bytes (If Applicable) 1 byte at a time */ - ptr8 = (char *) ptr32; - for ( n = total_bytes % sizeof( int32_t ); n > 0; n-- ) - { - if ( *ptr8++ != (char) fill_value ) - { - total_bytes_used++; - } - - /* Update Value */ - fill_value >>= 8; - } - - return total_bytes_used; -} - -/*-------------------------------------------------------------------* - * mem_check_OOB() - * - * Checks, if out-of-bounds access has occured. This is done by inspecting the 'signature' value - * taht has been added before and after the memory block during its allocation - *--------------------------------------------------------------------*/ - -static unsigned int mem_check_OOB( allocator_record *record_ptr ) -{ - int32_t *ptr32; - unsigned int OOB_Flag = 0x0; - int32_t mask; - size_t i; - int f; - - ptr32 = (int32_t *) record_ptr->block_ptr - N_32BITS_BLOCKS; - - /* Check the Signature at the Beginning of Memory Block */ - i = N_32BITS_BLOCKS; - do - { - if ( *ptr32++ ^ MAGIC_VALUE_OOB ) - { - OOB_Flag |= OOB_START; - } - } while ( --i ); - - /* Advance to End (Snap to lowest 32 Bits) */ - ptr32 += record_ptr->block_size / sizeof( int32_t ); - - /* Calculate Unused Space That has been added to get to the rounded Block Size */ - i = ROUND_BLOCK_SIZE( record_ptr->block_size ) - record_ptr->block_size; - - /* Partial Check of Signature at the End of Memory Block (for block size that has been rounded) */ - f = i % sizeof( int32_t ); - if ( f != 0 ) - { - mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); - if ( ( *ptr32++ ^ MAGIC_VALUE_OOB ) & mask ) - { - OOB_Flag |= OOB_END; - } - } - - /* Full Check of Signature at the End of Memory Block, i.e. all 32 Bits (for the remainder after rounding) */ - i /= sizeof( int32_t ); - i += N_32BITS_BLOCKS; - do - { - if ( *ptr32++ ^ MAGIC_VALUE_OOB ) - { - OOB_Flag |= OOB_END; - } - } while ( --i ); - - return OOB_Flag; -} - -/*-------------------------------------------------------------------* - * malloc_hash() - * - * Calculate hash from function name, line number and malloc size - *--------------------------------------------------------------------*/ - -static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str ) -{ - unsigned long hash = 5381; - const char *ptr_str; - - ptr_str = func_name; - while ( ptr_str != NULL && *ptr_str != '\0' ) - { - hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */ - } - - hash = ( ( hash << 5 ) + hash ) + func_lineno; /* hash * 33 + func_lineno */ - - ptr_str = size_str; - while ( ptr_str != NULL && *ptr_str != '\0' ) - { - hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */ - } - - return hash; -} - -/*-------------------------------------------------------------------* - * get_mem_record() - * - * Search for memory record in the internal list, return NULL if not found - * Start from index_record - *--------------------------------------------------------------------*/ - -allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record ) -{ - int i; - - if ( *index_record < 0 || *index_record > Num_Records ) - { - return NULL; - } - - /* calculate hash */ - *hash = malloc_hash( func_name, func_lineno, size_str ); - - for ( i = *index_record; i < Num_Records; i++ ) - { - /* check, if memory block is not allocated at the moment and the hash matches */ - if ( allocation_list[i].block_ptr == NULL && *hash == allocation_list[i].hash ) - { - *index_record = i; - return &( allocation_list[i] ); - } - } - - /* not found */ - *index_record = -1; - return NULL; -} - - -/*-------------------------------------------------------------------* - * mem_free() - * - * This function de-allocatesd the memory block and frees the mphysical memory with free(). - * It also updates actual and average usage of the memory block. - * - * Note: The record is not removed from the list and may be reused later on in mem_alloc()! - *--------------------------------------------------------------------*/ - -void mem_free( const char *func_name, int func_lineno, void *ptr ) -{ - int i, index_record; - char *tmp_ptr; - allocator_record *ptr_record; - - /* Search for the Block Pointer in the List */ - ptr_record = NULL; - index_record = -1; - for ( i = 0; i < Num_Records; i++ ) - { - if ( ptr == allocation_list[i].block_ptr ) - { /* Yes, Found it */ - ptr_record = &( allocation_list[i] ); - index_record = i; - break; - } - } - - if ( ptr_record == NULL ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Unable to Find Record Corresponding to the Allocated Memory Block!" ); - exit( -1 ); - } - - /* Update the Heap Size */ - current_heap_size -= ptr_record->block_size; - - /* Calculate the Actual Usage of the Memory Block (Look for Signature) */ - ptr_record->total_used_size += mem_set_usage( ptr_record ); - - /* Check, if Out-Of-Bounds Access has been Detected */ - ptr_record->OOB_Flag = mem_check_OOB( ptr_record ); - -#ifdef MEM_COUNT_DETAILS - /* Export heap memory de-allocation record to the .csv file */ - fprintf( fid_csv_filename, "D,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); -#endif - - /* De-Allocate Memory Block */ - tmp_ptr = (char *) ptr; - tmp_ptr -= BLOCK_ROUNDING; - ptr = (void *) tmp_ptr; - free( ptr ); - - /* Add new entry to the heap allocation call tree */ - if ( heap_allocation_call_tree == NULL ) - { - fprintf( stderr, "Error: Heap allocation call tree not created!" ); - exit( -1 ); - } - - /* check, if the maximum size of the call tree has been reached -> resize if so */ - if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size ) - { - heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP; - heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) ); - } - - heap_allocation_call_tree[heap_allocation_call_tree_size++] = -index_record; - - /* Reset memory block pointer (this is checked when updating wc intra-frame and inter-frame memory) */ - ptr_record->block_ptr = NULL; - - return; -} - - -/*-------------------------------------------------------------------* - * update_mem() - * - * This function updates the worst-case intra-frame memory and the worst-case inter-frame memory. - *--------------------------------------------------------------------*/ - -void update_mem( void ) -{ - int i, j, flag_alloc = -1, i_record; - int size_current_intra_frame_heap; - int *list_current_intra_frame_heap = NULL, n_items_current_intra_frame_heap; - allocator_record *ptr_record; - - /* process the heap allocation call tree and prepare lists of intra-frame and inter-frame heap memory blocks for this frame */ - n_items_current_intra_frame_heap = 0; - size_current_intra_frame_heap = 0; - for ( i = 0; i < heap_allocation_call_tree_size; i++ ) - { - /* get the record */ - i_record = heap_allocation_call_tree[i]; - - if ( i_record > 0 ) - { - flag_alloc = 1; - } - else if ( i_record < 0 ) - { - flag_alloc = 0; - i_record = -i_record; - } - ptr_record = &( allocation_list[i_record] ); - - if ( ptr_record->frame_allocated == update_cnt && ptr_record->block_ptr == NULL ) - { - /* intra-frame heap memory */ - if ( list_current_intra_frame_heap == NULL ) - { - list_current_intra_frame_heap = (int *) malloc( heap_allocation_call_tree_size * sizeof( int ) ); - memset( list_current_intra_frame_heap, -1, heap_allocation_call_tree_size * sizeof( int ) ); - } - - /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ - if ( i_record == 0 ) - { - flag_alloc = 1; - for ( j = 0; j < n_items_current_intra_frame_heap; j++ ) - { - if ( list_current_intra_frame_heap[j] == i_record ) - { - flag_alloc = 0; - break; - } - } - } - - if ( flag_alloc ) - { - /* add to list */ - list_current_intra_frame_heap[n_items_current_intra_frame_heap++] = i_record; - size_current_intra_frame_heap += ptr_record->block_size; - - /* no need to re-size the list -> the initially allocated size should be large enough */ - } - else - { - /* remove from list */ - for ( j = 0; j < n_items_current_intra_frame_heap; j++ ) - { - if ( list_current_intra_frame_heap[j] == i_record ) - { - break; - } - } - memmove( &list_current_intra_frame_heap[j], &list_current_intra_frame_heap[j + 1], ( n_items_current_intra_frame_heap - j ) * sizeof( int ) ); - n_items_current_intra_frame_heap--; - size_current_intra_frame_heap -= ptr_record->block_size; - - /* reset block size */ - ptr_record->frame_allocated = -1; - ptr_record->block_size = 0; - } - } - else - { - /* inter-frame heap memory */ - - /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ - if ( i_record == 0 ) - { - flag_alloc = 1; - for ( j = 0; j < n_items_current_inter_frame_heap; j++ ) - { - if ( list_current_inter_frame_heap[j] == i_record ) - { - flag_alloc = 0; - break; - } - } - } - - if ( flag_alloc ) - { - /* add to list */ - if ( n_items_current_inter_frame_heap >= max_items_current_inter_frame_heap ) - { - /* resize list, if needed */ - max_items_current_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; - list_current_inter_frame_heap = realloc( list_current_inter_frame_heap, max_items_current_inter_frame_heap * sizeof( int ) ); - } - - list_current_inter_frame_heap[n_items_current_inter_frame_heap++] = i_record; - size_current_inter_frame_heap += ptr_record->block_size; - } - else - { - /* remove from list */ - for ( j = 0; j < n_items_current_inter_frame_heap; j++ ) - { - if ( list_current_inter_frame_heap[j] == i_record ) - { - break; - } - } - memmove( &list_current_inter_frame_heap[j], &list_current_inter_frame_heap[j + 1], ( n_items_current_inter_frame_heap - j ) * sizeof( int ) ); - n_items_current_inter_frame_heap--; - size_current_inter_frame_heap -= ptr_record->block_size; - - /* reset block size */ - ptr_record->frame_allocated = -1; - ptr_record->block_size = 0; - } - } - } - - /* check, if this is the new worst-case for intra-frame heap memory */ - if ( size_current_intra_frame_heap > size_wc_intra_frame_heap ) - { - if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap ) - { - /* resize the list, if needed */ - max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; - list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) ); - } - - /* copy current-frame list to worst-case list */ - memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) ); - n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap; - size_wc_intra_frame_heap = size_current_intra_frame_heap; - location_wc_intra_frame_heap = update_cnt; - - /* update the wc numbers in all individual records */ - for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) - { - i_record = list_wc_intra_frame_heap[i]; - ptr_record = &( allocation_list[i_record] ); - ptr_record->wc_heap_size_intra_frame = ptr_record->block_size; - } - } - - /* check, if this is the new worst-case for inter-frame heap memory */ - if ( size_current_inter_frame_heap > size_wc_inter_frame_heap ) - { - if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap ) - { - /* resize list, if needed */ - max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; - list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) ); - } - - /* copy current-frame list to worst-case list */ - memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) ); - n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap; - size_wc_inter_frame_heap = size_current_inter_frame_heap; - location_wc_inter_frame_heap = update_cnt; - - /* update the wc numbers in all individual records */ - for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) - { - i_record = list_wc_inter_frame_heap[i]; - ptr_record = &( allocation_list[i_record] ); - ptr_record->wc_heap_size_inter_frame = ptr_record->block_size; - } - } - - /* reset heap allocation call tree */ - heap_allocation_call_tree_size = 0; - - /* de-allocate list of intra-frame heap memory blocks in the current fraeme - it's needed only inside this function */ - if ( list_current_intra_frame_heap ) - { - free( list_current_intra_frame_heap ); - } - - return; -} - -#ifdef MEM_COUNT_DETAILS -/*-------------------------------------------------------------------* - * subst() - * - * Substitute character in string - *--------------------------------------------------------------------*/ - -static void subst( char *s, char from, char to ) -{ - while ( *s == from ) - { - *s++ = to; - } - - return; -} - - -/*-------------------------------------------------------------------* - * mem_count_summary() - * - * Print detailed (per-item) information about heap memory usage - *--------------------------------------------------------------------*/ - -static void mem_count_summary( void ) -{ - int i, j, index, index_record; - size_t length; - char buf[300], format_str[50], name_str[MAX_FUNCTION_NAME_LENGTH + 3], parms_str[MAX_PARAMS_LENGTH + 1], type_str[10], usage_str[20], size_str[20], line_str[10]; - allocator_record *ptr_record, *ptr; - - /* Prepare format string */ - sprintf( format_str, "%%-%ds %%5s %%6s %%-%ds %%20s %%6s ", MAX_FUNCTION_NAME_LENGTH, MAX_PARAMS_LENGTH ); - - if ( n_items_wc_intra_frame_heap > 0 ) - { - /* Intra-Frame Heap Size */ - fprintf( stdout, "\nList of memory blocks when maximum intra-frame heap size is reached:\n\n" ); - - /* Find duplicate records (same hash and worst-case heap size) */ - for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) - { - index_record = list_wc_intra_frame_heap[i]; - if ( index_record == -1 ) - { - continue; - } - - ptr_record = &( allocation_list[index_record] ); - for ( j = i + 1; j < n_items_wc_intra_frame_heap; j++ ) - { - index = list_wc_intra_frame_heap[j]; - if ( index == -1 ) - { - continue; - } - ptr = &( allocation_list[index] ); - - if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_intra_frame == ptr_record->wc_heap_size_intra_frame ) - { - ptr_record->noccurances++; - list_wc_intra_frame_heap[j] = -1; - } - } - } - - /* Print Header */ - sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Maximum Size", "Usage" ); - puts( buf ); - length = strlen( buf ); - sprintf( buf, "%0*d\n", (int) length - 1, 0 ); - subst( buf, '0', '-' ); - puts( buf ); - - for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) - { - index_record = list_wc_intra_frame_heap[i]; - - if ( index_record != -1 ) - { - /* get the record */ - ptr_record = &( allocation_list[index_record] ); - - /* prepare information strings */ - strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH ); - strcat( name_str, "()" ); - name_str[MAX_FUNCTION_NAME_LENGTH] = '\0'; - strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH ); - parms_str[MAX_PARAMS_LENGTH] = '\0'; - - if ( ptr_record->params[0] == 'm' ) - { - strcpy( type_str, "malloc" ); - } - else - { - strcpy( type_str, "calloc" ); - } - - sprintf( line_str, "%d", ptr_record->lineno ); - - /* prepare average usage & memory size strings */ - sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 1 ) ) * 100.0f ) ); - - if ( ptr_record->noccurances > 1 ) - { - sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); - } - else - { - sprintf( size_str, "%d %s", (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); - } - - sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str ); - puts( buf ); - } - } - - fprintf( stdout, "\n" ); - } - - if ( n_items_wc_inter_frame_heap > 0 ) - { - /* Inter-Frame Heap Size */ - fprintf( stdout, "\nList of memory blocks when maximum inter-frame heap size is reached:\n\n" ); - - /* Find duplicate records (same hash and worst-case heap size) */ - for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) - { - index_record = list_wc_inter_frame_heap[i]; - if ( index_record == -1 ) - { - continue; - } - ptr_record = &( allocation_list[index_record] ); - ptr_record->noccurances = 1; /* reset the counter as some blocks may have been both, intra-frame and inter-frame */ - for ( j = i + 1; j < n_items_wc_inter_frame_heap; j++ ) - { - index = list_wc_inter_frame_heap[j]; - if ( index == -1 ) - { - continue; - } - ptr = &( allocation_list[index] ); - - if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_inter_frame == ptr_record->wc_heap_size_inter_frame ) - { - ptr_record->noccurances++; - list_wc_inter_frame_heap[j] = -1; - } - } - } - - /* Print Header */ - sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Memory Size", "Usage" ); - puts( buf ); - length = strlen( buf ); - sprintf( buf, "%0*d\n", (int) length - 1, 0 ); - subst( buf, '0', '-' ); - puts( buf ); - - for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) - { - index_record = list_wc_inter_frame_heap[i]; - - if ( index_record != -1 ) - { - /* get the record */ - ptr_record = &( allocation_list[index_record] ); - - /* prepare information strings */ - strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH ); - strcat( name_str, "()" ); - name_str[MAX_FUNCTION_NAME_LENGTH] = '\0'; - strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH ); - parms_str[MAX_PARAMS_LENGTH] = '\0'; - - if ( ptr_record->params[0] == 'm' ) - { - strcpy( type_str, "malloc" ); - } - else - { - strcpy( type_str, "calloc" ); - } - - sprintf( line_str, "%d", ptr_record->lineno ); - - /* prepare average usage & memory size strings */ - sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 0.1f ) ) * 100.0f + 0.5f ) ); - - if ( ptr_record->noccurances > 1 ) - { - sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); - } - else - { - sprintf( size_str, "%d %s", (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); - } - - sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str ); - puts( buf ); - } - } - - fprintf( stdout, "\n" ); - } - - return; -} - -#endif - -/*-------------------------------------------------------------------* - * print_mem() - * - * Print information about ROM and RAM memory usage - *--------------------------------------------------------------------*/ - -void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) -{ - int i, nElem; - - fprintf( stdout, "\n\n --- Memory usage --- \n\n" ); - - if ( Const_Data_PROM_Table != NULL ) - { - nElem = 0; - while ( strcmp( Const_Data_PROM_Table[nElem].file_spec, "" ) != 0 ) - nElem++; - - for ( i = 0; i < nElem; i++ ) - { - fprintf( stdout, "Program ROM size (%s): %d instruction words\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size ); - } - - for ( i = 0; i < nElem; i++ ) - { - if ( Const_Data_PROM_Table[i].Get_Const_Data_Size_Func == NULL ) - { - fprintf( stdout, "Error: Cannot retrieve or calculate Table ROM size of (%s)!\n", Const_Data_PROM_Table[i].file_spec ); - } - - fprintf( stdout, "Table ROM (const data) size (%s): %d %s\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].Get_Const_Data_Size_Func() >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); - } - } - else - { - fprintf( stdout, "Program ROM size: not available\n" ); - fprintf( stdout, "Table ROM (const data) size: not available\n" ); - } - - if ( wc_ram_size > 0 ) - { - fprintf( stdout, "Maximum RAM (stack + heap) size: %d %s in frame %d\n", wc_ram_size >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], wc_ram_frame ); - } - else - { - fprintf( stdout, "Maximum RAM (stack + heap) size: not available\n" ); - } - - /* check, if the stack is empty */ - if ( ptr_current_stack != ptr_base_stack ) - { - fprintf( stderr, "Warning: Stack is not empty.\n" ); - } - - if ( ptr_base_stack - ptr_max_stack > 0 ) - { - fprintf( stdout, "Maximum stack size: %lu %s in frame %d\n", ( ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], - wc_stack_frame ); - } - else - { - fprintf( stdout, "Maximum stack size: not available\n" ); - } - - /* last update of intra-frame memory and inter-frame memory, if needed */ - if ( heap_allocation_call_tree_size > 0 ) - { - update_mem(); - } - - /* check, if all memory blocks have been deallocated (freed) */ - for ( i = 0; i < Num_Records; i++ ) - { - if ( allocation_list[i].block_ptr != NULL ) - { - fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", allocation_list[i].name, allocation_list[i].lineno, "Error: Memory Block has not been De-Allocated with free()!" ); - exit( -1 ); - } - } - - if ( n_items_wc_intra_frame_heap > 0 ) - { - fprintf( stdout, "Maximum intra-frame heap size: %d %s in frame %d\n", size_wc_intra_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_intra_frame_heap ); - } - else - { - fprintf( stdout, "Maximum intra-frame heap size: 0\n" ); - } - - if ( n_items_wc_inter_frame_heap > 0 ) - { - fprintf( stdout, "Maximum inter-frame heap size: %d %s in frame %d\n", size_wc_inter_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_inter_frame_heap ); - } - else - { - fprintf( stdout, "Maximum inter-frame heap size: 0\n" ); - } - -#ifdef MEM_COUNT_DETAILS - /* Print detailed information about worst-case stack usage */ - if ( ptr_base_stack - ptr_max_stack > 0 ) - { - print_stack_call_tree(); - } - - /* Print detailed information about worst-case heap usage */ - mem_count_summary(); -#endif - - if ( Stat_Cnt_Size > 0 ) - { - fprintf( stdout, "\nNote: 1 word = %d bits\n", 8 << Stat_Cnt_Size ); - fprintf( stdout, "This is an optimistic estimate of memory consumption assuming that each variable type is stored with sizeof(type) bits\n" ); - } - - if ( n_items_wc_intra_frame_heap > 0 ) - { - fprintf( stdout, "Intra-frame heap memory is allocated and de-allocated in the same frame\n" ); - } - - /* De-allocate list of heap memory blocks */ - if ( allocation_list != NULL ) - { - free( allocation_list ); - } - - /* De-allocate heap allocation call tree */ - if ( heap_allocation_call_tree != NULL ) - { - free( heap_allocation_call_tree ); - } - - /* De-allocate intra-frame and inter-frame heap lists */ - if ( list_wc_intra_frame_heap != NULL ) - { - free( list_wc_intra_frame_heap ); - } - - if ( list_current_inter_frame_heap != NULL ) - { - free( list_current_inter_frame_heap ); - } - - if ( list_wc_inter_frame_heap != NULL ) - { - free( list_wc_inter_frame_heap ); - } - -#ifdef MEM_COUNT_DETAILS - if ( fid_csv_filename != NULL ) - { - fclose( fid_csv_filename ); - } -#endif - - return; -} - -#endif /* WMOPS */ - -#ifndef WMOPS -int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */ -#endif - - \ No newline at end of file +/* + * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved. + * + * This software is protected by copyright law and by international treaties. The source code, and all of its derivations, + * is provided by VoiceAge Corporation under the "ITU-T Software Tools' General Public License". Please, read the license file + * or refer to ITU-T Recommendation G.191 on "SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS". + * + * Any use of this software is permitted provided that this notice is not removed and that neither the authors nor + * VoiceAge Corporation are deemed to have made any representations as to the suitability of this software + * for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE. + * + * Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca) + */ + +#include +#include +#include +#include +#include + +#ifndef _MSC_VER +#include +#include +#else +#include +#endif + +#include "wmc_auto.h" + +#define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */ + +#ifdef WMOPS + +/*-------------------------------------------------------------------* + * Complexity counting tool + *--------------------------------------------------------------------*/ + +#define MAX_FUNCTION_NAME_LENGTH 50 /* Maximum length of the function name */ +#define MAX_PARAMS_LENGTH 50 /* Maximum length of the function parameter string */ +#define MAX_NUM_RECORDS 300 /* Initial maximum number of records -> mightb be increased during runtime, if needed */ +#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of records, increase the number of records by this number */ + +#define MAX_CALL_TREE_DEPTH 100 /* maximum depth of the function call tree */ +#define DOUBLE_MAX 0x80000000 + +typedef struct +{ + char label[MAX_FUNCTION_NAME_LENGTH]; + long call_number; + long update_cnt; + int call_tree[MAX_CALL_TREE_DEPTH]; + double start_selfcnt; + double current_selfcnt; + double max_selfcnt; + double min_selfcnt; + double tot_selfcnt; + double start_cnt; + double current_cnt; + double max_cnt; + double min_cnt; + double tot_cnt; +#ifdef WMOPS_WC_FRAME_ANALYSIS + int32_t current_call_number; + double wc_cnt; + double wc_selfcnt; + int32_t wc_call_number; +#endif +} wmops_record; + +double ops_cnt; +double prom_cnt; +double inst_cnt[NUM_INST]; + +static wmops_record *wmops = NULL; +static int num_wmops_records, max_num_wmops_records; +static int current_record; +static long update_cnt; +static double start_cnt; +static double max_cnt; +static double min_cnt; +static double inst_cnt_wc[NUM_INST]; +static long fnum_cnt_wc; +static int *wmops_caller_stack = NULL, wmops_caller_stack_index, max_wmops_caller_stack_index = 0; +static int *heap_allocation_call_tree = NULL, heap_allocation_call_tree_size = 0, heap_allocation_call_tree_max_size = 0; + +void reset_wmops( void ) +{ + int i, j; + + num_wmops_records = 0; + max_num_wmops_records = MAX_NUM_RECORDS; + current_record = -1; + update_cnt = 0; + + max_cnt = 0.0; + min_cnt = DOUBLE_MAX; + start_cnt = 0.0; + ops_cnt = 0.0; + + /* allocate the list of wmops records */ + if ( wmops == NULL ) + { + wmops = (wmops_record *)malloc( max_num_wmops_records * sizeof( wmops_record ) ); + } + + if ( wmops == NULL ) + { + fprintf( stderr, "Error: Unable to Allocate List of WMOPS Records!" ); + exit( -1 ); + } + + for ( i = 0; i < max_num_wmops_records; i++ ) + { + strcpy( &wmops[i].label[0], "\0" ); + wmops[i].call_number = 0; + wmops[i].update_cnt = 0; + for ( j = 0; j < MAX_CALL_TREE_DEPTH; j++ ) + { + wmops[i].call_tree[j] = -1; + } + wmops[i].start_selfcnt = 0.0; + wmops[i].current_selfcnt = 0.0; + wmops[i].max_selfcnt = 0.0; + wmops[i].min_selfcnt = DOUBLE_MAX; + wmops[i].tot_selfcnt = 0.0; + wmops[i].start_cnt = 0.0; + wmops[i].current_cnt = 0.0; + wmops[i].max_cnt = 0.0; + wmops[i].min_cnt = DOUBLE_MAX; + wmops[i].tot_cnt = 0.0; +#ifdef WMOPS_WC_FRAME_ANALYSIS + wmops[i].wc_cnt = 0.0; + wmops[i].wc_selfcnt = 0.0; + wmops[i].current_call_number = 0; + wmops[i].wc_call_number = -1; +#endif + } + + /* allocate the list of wmops callers to track the sequence of function calls */ + wmops_caller_stack_index = 0; + max_wmops_caller_stack_index = MAX_NUM_RECORDS; + if ( wmops_caller_stack == NULL ) + { + wmops_caller_stack = malloc( max_wmops_caller_stack_index * sizeof( int ) ); + } + + if ( wmops_caller_stack == NULL ) + { + fprintf( stderr, "Error: Unable to Allocate List of WMOPS Callers!" ); + exit( -1 ); + } + + for ( i = 0; i < max_wmops_caller_stack_index; i++ ) + { + wmops_caller_stack[i] = -1; + } + + return; +} + + +void push_wmops( const char *label ) +{ + int new_flag; + int i, j; + + /* Check, if this is a new function label */ + new_flag = 1; + for ( i = 0; i < num_wmops_records; i++ ) + { + if ( strcmp( wmops[i].label, label ) == 0 ) + { + new_flag = 0; + break; + } + } + + /* Create a new record in the list */ + if ( new_flag ) + { + if ( num_wmops_records >= max_num_wmops_records ) + { + /* There is no room for a new wmops record -> reallocate the list */ + max_num_wmops_records += MAX_NUM_RECORDS_REALLOC_STEP; + wmops = realloc( wmops, max_num_wmops_records * sizeof( wmops_record ) ); + } + + strcpy( wmops[i].label, label ); + num_wmops_records++; + } + + /* Push the current context info to the new record */ + if ( current_record >= 0 ) + { + if ( wmops_caller_stack_index >= max_wmops_caller_stack_index ) + { + /* There is no room for a new record -> reallocate the list */ + max_wmops_caller_stack_index += MAX_NUM_RECORDS_REALLOC_STEP; + wmops_caller_stack = realloc( wmops_caller_stack, max_wmops_caller_stack_index * sizeof( int ) ); + } + wmops_caller_stack[wmops_caller_stack_index++] = current_record; + + /* accumulate op counts */ + wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; + + /* update call tree */ + for ( j = 0; j < MAX_CALL_TREE_DEPTH; j++ ) + { + if ( wmops[i].call_tree[j] == current_record ) + { + break; + } + else if ( wmops[i].call_tree[j] == -1 ) + { + wmops[i].call_tree[j] = current_record; + break; + } + } + } + + /* update the current context info */ + current_record = i; + wmops[current_record].start_selfcnt = ops_cnt; + wmops[current_record].start_cnt = ops_cnt; + wmops[current_record].call_number++; +#ifdef WMOPS_WC_FRAME_ANALYSIS + wmops[current_record].current_call_number++; +#endif + + return; +} + + +void pop_wmops( void ) +{ + + /* Check for underflow */ + if ( current_record < 0 ) + { + fprintf( stdout, "\r pop_wmops(): stack underflow, too many calls to pop_wmops()\n" ); + exit( -1 ); + } + + /* update count of current record */ + wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; + wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt; + + /* Get back previous context from stack */ + if ( wmops_caller_stack_index > 0 ) + { + current_record = wmops_caller_stack[--wmops_caller_stack_index]; + wmops[current_record].start_selfcnt = ops_cnt; + } + else + { + current_record = -1; + } + + return; +} + + +void update_wmops( void ) +{ + int i; + double current_cnt; +#ifdef WMOPS_PER_FRAME + static FILE *fid = NULL; + const char filename[] = "wmops_analysis"; + float tmpF; +#endif + + if ( wmops_caller_stack_index != 0 ) + { + fprintf( stdout, "update_wmops(): WMOPS caller stack corrupted - check that all push_wmops() are matched with pop_wmops()!\n" ); + exit( -1 ); + } + +#ifdef WMOPS_PER_FRAME + /* Check, if the output file has already been opened */ + if ( fid == NULL ) + { + fid = fopen( filename, "wb" ); + + if ( fid == NULL ) + { + fprintf( stderr, "\nCannot open %s!\n\n", filename ); + exit( -1 ); + } + } + + /* Write current complexity to the external file */ + tmpF = (float) ( FAC * wmops[0].current_cnt ); + fwrite( &tmpF, sizeof( float ), 1, fid ); +#endif + +#ifdef WMOPS_WC_FRAME_ANALYSIS + if ( ops_cnt - start_cnt > max_cnt ) + { + for ( i = 0; i < num_wmops_records; i++ ) + { + wmops[i].wc_cnt = wmops[i].current_cnt; + wmops[i].wc_selfcnt = wmops[i].current_selfcnt; + wmops[i].wc_call_number = wmops[i].current_call_number; + } + } +#endif + + for ( i = 0; i < num_wmops_records; i++ ) + { + wmops[i].tot_selfcnt += wmops[i].current_selfcnt; + wmops[i].tot_cnt += wmops[i].current_cnt; + + if ( wmops[i].current_selfcnt > 0 ) + { + if ( wmops[i].current_selfcnt > wmops[i].max_selfcnt ) + { + wmops[i].max_selfcnt = wmops[i].current_selfcnt; + } + + if ( wmops[i].current_selfcnt < wmops[i].min_selfcnt ) + { + wmops[i].min_selfcnt = wmops[i].current_selfcnt; + } + } + + wmops[i].current_selfcnt = 0; + + if ( wmops[i].current_cnt > 0 ) + { + if ( wmops[i].current_cnt > wmops[i].max_cnt ) + { + wmops[i].max_cnt = wmops[i].current_cnt; + } + + if ( wmops[i].current_cnt < wmops[i].min_cnt ) + { + wmops[i].min_cnt = wmops[i].current_cnt; + } + + wmops[i].update_cnt++; + } + + wmops[i].current_cnt = 0; +#ifdef WMOPS_WC_FRAME_ANALYSIS + wmops[i].current_call_number = 0; +#endif + } + + current_cnt = ops_cnt - start_cnt; + if ( current_cnt > max_cnt ) + { + max_cnt = current_cnt; + + for ( i = 0; i < NUM_INST; i++ ) + { + inst_cnt_wc[i] = inst_cnt[i]; + } + + fnum_cnt_wc = update_cnt + 1; + } + + if ( current_cnt < min_cnt ) + { + min_cnt = current_cnt; + } + + for ( i = 0; i < NUM_INST; i++ ) + { + inst_cnt[i] = 0.0; + } + + start_cnt = ops_cnt; + + /* increment frame counter */ + update_cnt++; + + return; +} + + +void print_wmops( void ) +{ + int i, label_len, max_label_len; + + char *sfmts = "%*s %8s %8s %7s %7s\n"; + char *dfmts = "%*s %8.2f %8.3f %7.3f %7.3f\n"; + char *sfmt = "%*s %8s %8s %7s %7s %7s %7s %7s\n"; + char *dfmt = "%*s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\n"; + +#ifdef WMOPS_WC_FRAME_ANALYSIS + int j; + char *sfmtt = "%20s %4s %15s\n"; + char *dfmtt = "%20s %4d "; +#endif + + /* calculate maximum label length for compact prinout */ + max_label_len = 0; + for ( i = 0; i < num_wmops_records; i++ ) + { + label_len = strlen( wmops[i].label ); + if ( label_len > max_label_len ) + { + max_label_len = label_len; + } + } + max_label_len += 4; + + fprintf( stdout, "\n\n --- Complexity analysis [WMOPS] --- \n\n" ); + + fprintf( stdout, "%*s %33s %23s\n", max_label_len, "", "|------ SELF ------|", "|--- CUMULATIVE ---|" ); + fprintf( stdout, sfmt, max_label_len, " routine", " calls", " min ", " max ", " avg ", " min ", " max ", " avg " ); + fprintf( stdout, sfmt, max_label_len, "---------------", "------", "------", "------", "------", "------", "------", "------" ); + + for ( i = 0; i < num_wmops_records; i++ ) + { + fprintf( stdout, dfmt, max_label_len, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt, + wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt, + FAC * wmops[i].max_selfcnt, + wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_selfcnt / wmops[i].update_cnt, + wmops[i].min_cnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_cnt, + FAC * wmops[i].max_cnt, + wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_cnt / wmops[i].update_cnt ); + } + + fprintf( stdout, sfmts, max_label_len, "---------------", "------", "------", "------", "------" ); + fprintf( stdout, dfmts, max_label_len, "total", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt ); + fprintf( stdout, "\n" ); + +#ifdef WMOPS_WC_FRAME_ANALYSIS + fprintf( stdout, "\nComplexity analysis for the worst-case frame %ld:\n\n", fnum_cnt_wc ); + fprintf( stdout, "%*s %8s %10s %12s\n", max_label_len, " routine", " calls", " SELF", " CUMULATIVE" ); + fprintf( stdout, "%*s %8s %10s %10s\n", max_label_len, "---------------", "------", "------", "----------" ); + + for ( i = 0; i < num_wmops_records; i++ ) + { + if ( wmops[i].wc_call_number > 0 ) + { + fprintf( stdout, "%*s %8d %10.3f %12.3f\n", max_label_len, wmops[i].label, wmops[i].wc_call_number, FAC * wmops[i].wc_selfcnt, FAC * wmops[i].wc_cnt ); + } + } + + fprintf( stdout, "\nCall tree for the worst-case frame %ld:\n\n", fnum_cnt_wc ); + fprintf( stdout, sfmtt, " function", "num", "called by " ); + fprintf( stdout, sfmtt, "---------------", "---", "--------------" ); + + for ( i = 0; i < num_wmops_records; i++ ) + { + if ( wmops[i].wc_call_number > 0 ) + { + fprintf( stdout, dfmtt, wmops[i].label, i ); + for ( j = 0; wmops[i].call_tree[j] != -1 && j < MAX_CALL_TREE_DEPTH; j++ ) + { + if ( j != 0 ) + { + fprintf( stdout, ", " ); + } + fprintf( stdout, "%d", wmops[i].call_tree[j] ); + } + fprintf( stdout, "\n" ); + } + } + + fprintf( stdout, "\n\n" ); + + fprintf( stdout, "\nInstruction type analysis for the worst-case frame %ld:\n\n", fnum_cnt_wc ); + for ( i = 0; i < NUM_INST; i++ ) + { + switch ( (enum instructions) i ) + { + case _ADD: + fprintf( stdout, "\tAdds: %12.1f\n", inst_cnt_wc[i] ); + break; + case _ABS: + fprintf( stdout, "\tAbsolutes: %12.1f\n", inst_cnt_wc[i] ); + break; + case _MULT: + fprintf( stdout, "\tMultiplies: %12.1f\n", inst_cnt_wc[i] ); + break; + case _MAC: + fprintf( stdout, "\tMACs: %12.1f\n", inst_cnt_wc[i] ); + break; + case _MOVE: + fprintf( stdout, "\tMoves: %12.1f\n", inst_cnt_wc[i] ); + break; + case _STORE: + fprintf( stdout, "\tStores: %12.1f\n", inst_cnt_wc[i] ); + break; + case _LOGIC: + fprintf( stdout, "\tLogicals: %12.1f\n", inst_cnt_wc[i] ); + break; + case _SHIFT: + fprintf( stdout, "\tShifts: %12.1f\n", inst_cnt_wc[i] ); + break; + case _BRANCH: + fprintf( stdout, "\tBranches: %12.1f\n", inst_cnt_wc[i] ); + break; + case _DIV: + fprintf( stdout, "\tDivisions: %12.1f\n", inst_cnt_wc[i] ); + break; + case _SQRT: + fprintf( stdout, "\tSquare Root: %12.1f\n", inst_cnt_wc[i] ); + break; + case _TRANS: + fprintf( stdout, "\tTrans: %12.1f\n", inst_cnt_wc[i] ); + break; + case _FUNC: + fprintf( stdout, "\tFunc Call: %12.1f\n", inst_cnt_wc[i] ); + break; + case _LOOP: + fprintf( stdout, "\tLoop Init: %12.1f\n", inst_cnt_wc[i] ); + break; + case _INDIRECT: + fprintf( stdout, "\tIndirect Addr: %12.1f\n", inst_cnt_wc[i] ); + break; + case _PTR_INIT: + fprintf( stdout, "\tPointer Init: %12.1f\n", inst_cnt_wc[i] ); + break; + case _TEST: + fprintf( stdout, "\tExtra condit.: %12.1f\n", inst_cnt_wc[i] ); + break; + case _POWER: + fprintf( stdout, "\tExponential: %12.1f\n", inst_cnt_wc[i] ); + break; + case _LOG: + fprintf( stdout, "\tLogarithm: %12.1f\n", inst_cnt_wc[i] ); + break; + case _MISC: + fprintf( stdout, "\tAll other op.: %12.1f\n", inst_cnt_wc[i] ); + break; + default: + fprintf( stdout, "\tERROR: Invalid instruction type: %d\n\n", i ); + } + } +#endif + + return; +} + + +/*-------------------------------------------------------------------* + * Memory counting tool measuring RAM usage (stack and heap) + * + * Maximum RAM is measured by monitoring the total allocated memory (stack and heap) in each frame. + * + * Maximum stack is measured by monitoring the difference between the 'top' and 'bottom' of the stack. The 'bottom' of the stack is updated in each function + * with a macro 'func_start_' which is inserted automatically to all functions during the instrumentation process. + * + * Maximum heap is measured by summing the sizes of all memory blocks allocated by malloc() or calloc() and deallocated by free(). The maximum heap size is + * updated each time when the macros malloc_() or calloc_() is invoked. The macros 'malloc_ and calloc_' are inserted automatically during the instrumentation process. + * As part of heap measurements, intra-frame heap and inter-frame heap are measured separately. Intra-frame heap refers to heap memory which is allocated and deallocated + * within a single frame. Inter-frame heap, on the contrary, refers to heap memory which is reserved for more than one frame. + * + * In order to run the memory counting tool the function reset_mem(cnt_size) must be called at the beginning of the encoding/decoding process. + * The unit in which memory consumption is reported is set via the parameter 'cnt_size'. It can be set to 0 (bytes), 1 (32b words) or 2 (64b words). + * At the end of the encoding/decoding process, 'print_mem()' function may be called to print basic information about memory consumption. If the macro 'MEM_COUNT_DETAILS' + * is activated, detailed information is printed + * + * The macro 'WMOPS' needs to be activated to enable memory counting. To avoid the instrumentation of malloc()/calloc()/free() calls, use + * #define WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP macro pair around the malloc(), calloc() and free(). + *--------------------------------------------------------------------*/ + +/* This is the value (in bytes) towards which the block size is rounded. For example, a block of 123 bytes, when using + a 32 bits system, will end up taking 124 bytes since the last unused byte cannot be used for another block. */ +#ifdef MEM_ALIGN_64BITS +#define BLOCK_ROUNDING 8 /* Align on 64 Bits */ +#else +#define BLOCK_ROUNDING 4 /* Align on 32 Bits */ +#endif + +#define N_32BITS_BLOCKS ( BLOCK_ROUNDING / sizeof( int32_t ) ) +#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) ) + +#define MAGIC_VALUE_OOB 0x12A534F0 /* Signature value which is inserted before and after each allocated memory block, used to detect out-of-bound access */ +#define MAGIC_VALUE_USED ( ~MAGIC_VALUE_OOB ) /* Value used to pre-fill allocated memory blocks, used to calculate actual memory usage */ +#define OOB_START 0x1 /* Flag indicating out-of-bounds access before memory block */ +#define OOB_END 0x2 /* Flag indicating out-of-bounds access after memory block */ + +#ifdef MEM_COUNT_DETAILS +const char *csv_filename = "mem_analysis.csv"; +static FILE *fid_csv_filename = NULL; +#endif + +typedef struct +{ + char function_name[MAX_FUNCTION_NAME_LENGTH + 1]; + int16_t *stack_ptr; +} caller_info; + +static caller_info *stack_callers[2] = {NULL, NULL}; + +static int16_t *ptr_base_stack = 0; /* Pointer to the bottom of stack (base pointer). Stack grows up. */ +static int16_t *ptr_current_stack = 0; /* Pointer to the current stack pointer */ +static int16_t *ptr_max_stack = 0; /* Pointer to the maximum stack pointer (the farest point from the bottom of stack) */ +static int32_t wc_stack_frame = 0; /* Frame corresponding to the worst-case stack usage */ +static int current_calls = 0, max_num_calls = MAX_NUM_RECORDS; +static char location_max_stack[256] = "undefined"; + +/* Heap-related variables */ +typedef struct +{ + char name[MAX_FUNCTION_NAME_LENGTH + 1]; /* +1 for NUL */ + char params[1 + MAX_PARAMS_LENGTH + 1]; /* +1 for 'm'/'c' alloc & +1 for NUL */ + unsigned long hash; + int lineno; + void *block_ptr; + int block_size; + unsigned long total_block_size; /* Cumulative sum of the allocated size in the session */ + unsigned long total_used_size; /* Cumulative sum of the used size in the session */ + int wc_heap_size_intra_frame; /* Worst-Case Intra-Frame Heap Size */ + int wc_heap_size_inter_frame; /* Worst-Case Inter-Frame Heap Size */ + int frame_allocated; /* Frame number in which the Memory Block has been allocated (-1 if not allocated at the moment) */ + int OOB_Flag; + int noccurances; /* Number of times that the memory block has been allocated in a frame */ +} allocator_record; + +allocator_record *allocation_list = NULL; + +static int Num_Records, Max_Num_Records; +static size_t Stat_Cnt_Size = USE_BYTES; +static const char *Count_Unit[] = { "bytes", "words", "words" }; + +static int32_t wc_ram_size, wc_ram_frame; +static int32_t current_heap_size; +static int *list_wc_intra_frame_heap, n_items_wc_intra_frame_heap, max_items_wc_intra_frame_heap, size_wc_intra_frame_heap, location_wc_intra_frame_heap; +static int *list_current_inter_frame_heap, n_items_current_inter_frame_heap, max_items_current_inter_frame_heap, size_current_inter_frame_heap; +static int *list_wc_inter_frame_heap, n_items_wc_inter_frame_heap, max_items_wc_inter_frame_heap, size_wc_inter_frame_heap, location_wc_inter_frame_heap; + +/* Local Functions */ +static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str ); +allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record ); +static void *mem_alloc_block( size_t size, const char *size_str ); + +/*-------------------------------------------------------------------* + * reset_mem() + * + * Initialize/reset memory counting tool (stack and heap) + *--------------------------------------------------------------------*/ + +void reset_mem( Counting_Size cnt_size ) +{ + int16_t something; + size_t tmp_size; + + /* initialize list of stack records */ + if ( stack_callers[0] == NULL ) + { + stack_callers[0] = malloc( MAX_NUM_RECORDS * sizeof( caller_info ) ); + stack_callers[1] = malloc( MAX_NUM_RECORDS * sizeof( caller_info ) ); + } + + if ( stack_callers[0] == NULL || stack_callers[1] == NULL ) + { + fprintf( stderr, "Error: Unable to Allocate List of Stack Records!" ); + exit( -1 ); + } + + current_calls = 0; + max_num_calls = MAX_NUM_RECORDS; + + /* initialize stack pointers */ + ptr_base_stack = &something; + ptr_max_stack = ptr_base_stack; + ptr_current_stack = ptr_base_stack; + + /* initialize the unit of memory block size */ + Stat_Cnt_Size = cnt_size; + + /* Check, if sizeof(int32_t) is 4 bytes */ + tmp_size = sizeof( int32_t ); + if ( tmp_size != 4 ) + { + fprintf( stderr, "Error: Expecting 'int32_t' to be a 32 Bits Integer!" ); + exit( -1 ); + } + + /* create allocation list for malloc() memory blocks */ + if ( allocation_list == NULL ) + { + allocation_list = malloc( MAX_NUM_RECORDS * sizeof( allocator_record ) ); + } + + if ( allocation_list == NULL ) + { + fprintf( stderr, "Error: Unable to Create List of Memory Blocks!" ); + exit( -1 ); + } + + Num_Records = 0; + Max_Num_Records = MAX_NUM_RECORDS; + + wc_ram_size = 0; + wc_ram_frame = -1; + current_heap_size = 0; + + /* heap allocation tree */ + heap_allocation_call_tree_max_size = MAX_NUM_RECORDS; + if ( heap_allocation_call_tree == NULL ) + { + heap_allocation_call_tree = (int *) malloc( heap_allocation_call_tree_max_size * sizeof( int ) ); + memset( heap_allocation_call_tree, -1, heap_allocation_call_tree_max_size * sizeof( int ) ); + } + heap_allocation_call_tree_size = 0; + + /* wc intra-frame heap */ + max_items_wc_intra_frame_heap = MAX_NUM_RECORDS; + if ( list_wc_intra_frame_heap == NULL ) + { + list_wc_intra_frame_heap = (int *) malloc( max_items_wc_intra_frame_heap * sizeof( int ) ); + memset( list_wc_intra_frame_heap, -1, max_items_wc_intra_frame_heap * sizeof( int ) ); + } + n_items_wc_intra_frame_heap = 0; + size_wc_intra_frame_heap = 0; + location_wc_intra_frame_heap = -1; + + /* current inter-frame heap */ + max_items_current_inter_frame_heap = MAX_NUM_RECORDS; + if ( list_current_inter_frame_heap == NULL ) + { + list_current_inter_frame_heap = (int *) malloc( max_items_current_inter_frame_heap * sizeof( int ) ); + memset( list_current_inter_frame_heap, -1, max_items_current_inter_frame_heap * sizeof( int ) ); + } + n_items_current_inter_frame_heap = 0; + size_current_inter_frame_heap = 0; + + /* wc inter-frame heap */ + max_items_wc_inter_frame_heap = MAX_NUM_RECORDS; + if ( list_wc_inter_frame_heap == NULL ) + { + list_wc_inter_frame_heap = (int *) malloc( max_items_wc_inter_frame_heap * sizeof( int ) ); + memset( list_wc_inter_frame_heap, -1, max_items_wc_inter_frame_heap * sizeof( int ) ); + } + n_items_wc_inter_frame_heap = 0; + size_wc_inter_frame_heap = 0; + location_wc_inter_frame_heap = -1; + +#ifdef MEM_COUNT_DETAILS + /* Check, if the .csv file has already been opened */ + if ( fid_csv_filename == NULL ) + { + fid_csv_filename = fopen( csv_filename, "wb" ); + + if ( fid_csv_filename == NULL ) + { + fprintf( stderr, "\nCannot open %s!\n\n", csv_filename ); + exit( -1 ); + } + } + else + { + /* reset file */ + rewind( fid_csv_filename ); + } +#endif + + return; +} + +/*-------------------------------------------------------------------* + * reset_stack() + * + * Reset stack pointer + *--------------------------------------------------------------------*/ + +void reset_stack( void ) +{ + int16_t something; + + /* initialize/reset stack pointers */ + ptr_base_stack = &something; + ptr_max_stack = ptr_base_stack; + ptr_current_stack = ptr_base_stack; + + return; +} + +/*-------------------------------------------------------------------* + * push_stack() + * + * Check the current stack pointer and update the maximum stack pointer, if new maximum found. + *--------------------------------------------------------------------*/ + +int push_stack( const char *filename, const char *fctname ) +{ + int16_t something; + int32_t current_stack_size; + + ptr_current_stack = &something; + + (void) *filename; /* to avoid compilation warning */ + + if ( current_calls >= max_num_calls ) + { + /* There is no room for a new record -> reallocate the list */ + max_num_calls += MAX_NUM_RECORDS_REALLOC_STEP; + stack_callers[0] = realloc( stack_callers[0], max_num_calls * sizeof( caller_info ) ); + stack_callers[1] = realloc( stack_callers[1], max_num_calls * sizeof( caller_info ) ); + } + + /* Valid Function Name? */ + if ( fctname[0] == 0 ) + { /* No */ + fprintf( stderr, "Invalid function name for call stack info." ); + exit( -1 ); + } + + /* Save the Name of the Calling Function in the Table */ + strncpy( stack_callers[0][current_calls].function_name, fctname, MAX_FUNCTION_NAME_LENGTH ); + stack_callers[0][current_calls].function_name[MAX_FUNCTION_NAME_LENGTH] = 0; /* Nul Terminate */ + + /* Save the Stack Pointer */ + stack_callers[0][current_calls].stack_ptr = ptr_current_stack; + + /* Increase the Number of Calls in the List */ + current_calls++; + + /* Is this the First Time or the Worst Case? */ + if ( ptr_current_stack < ptr_max_stack || ptr_max_stack == NULL ) + { /* Yes */ + /* Save Info about it */ + ptr_max_stack = ptr_current_stack; + + /* save the worst-case frame number */ + /* current frame number is stored in the variable update_cnt and updated in the function update_wmops() */ + wc_stack_frame = update_cnt; + strncpy( location_max_stack, fctname, sizeof( location_max_stack ) - 1 ); + location_max_stack[sizeof( location_max_stack ) - 1] = '\0'; + + /* Save Call Tree */ + memmove( stack_callers[1], stack_callers[0], sizeof( caller_info ) * current_calls ); + + /* Terminate the List with 0 (for printing purposes) */ + if ( current_calls < max_num_calls ) + { + stack_callers[1][current_calls].function_name[0] = 0; + } + } + + /* Check, if This is the New Worst-Case RAM (stack + heap) */ + current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) ); + + if ( current_stack_size < 0 ) + { + /* prevent negative stack size */ + current_stack_size = 0; + } + + if ( current_stack_size + current_heap_size > wc_ram_size ) + { + wc_ram_size = current_stack_size + current_heap_size; + wc_ram_frame = update_cnt; + } + + return 0 /* for Now */; +} + +/*-------------------------------------------------------------------* + * pop_stack() + * + * Remove stack caller entry from the list + *--------------------------------------------------------------------*/ + +int pop_stack( const char *filename, const char *fctname ) +{ + caller_info *caller_info_ptr; + + (void) *filename; /* to avoid compilation warning */ + + /* Decrease the Number of Records */ + current_calls--; + + /* Get Pointer to Caller Information */ + caller_info_ptr = &stack_callers[0][current_calls]; + + /* Check, if the Function Names Match */ + if ( strncmp( caller_info_ptr->function_name, fctname, MAX_FUNCTION_NAME_LENGTH ) != 0 ) + { + fprintf( stderr, "Invalid usage of pop_stack()" ); + exit( -1 ); + } + + /* Erase Entry */ + caller_info_ptr->function_name[0] = 0; + + /* Retrieve previous stack pointer */ + if ( current_calls == 0 ) + { + ptr_current_stack = ptr_base_stack; + } + else + { + ptr_current_stack = stack_callers[0][current_calls - 1].stack_ptr; + } + + return 0 /* for Now */; +} + +#ifdef MEM_COUNT_DETAILS +/*-------------------------------------------------------------------* + * print_stack_call_tree() + * + * Print detailed information about worst-case stack usage + *--------------------------------------------------------------------*/ + +static void print_stack_call_tree( void ) +{ + caller_info *caller_info_ptr; + int call_level; + char fctname[MAX_FUNCTION_NAME_LENGTH + 1]; + + fprintf( stdout, "\nList of functions when maximum stack size is reached:\n\n" ); + + caller_info_ptr = &stack_callers[1][0]; + for ( call_level = 0; call_level < max_num_calls; call_level++ ) + { + /* Done? */ + if ( caller_info_ptr->function_name[0] == 0 ) + { + break; + } + + /* Print Name */ + strncpy( fctname, caller_info_ptr->function_name, MAX_FUNCTION_NAME_LENGTH ); + strcat( fctname, "()" ); + fprintf( stdout, "%-42s", fctname ); + + /* Print Stack Usage (Based on Difference) */ + if ( call_level != 0 ) + { + fprintf( stdout, "%lu %s\n", ( ( ( caller_info_ptr - 1 )->stack_ptr - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); + } + else + { + fprintf( stdout, "%lu %s\n", ( ( ptr_base_stack - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); + } + + /* Advance */ + caller_info_ptr++; + } + + fprintf( stdout, "\n" ); + + return; +} +#endif + + +/*-------------------------------------------------------------------* + * mem_alloc() + * + * Creates new record, stores auxiliary information about which function allocated the memory, line number, parameters, etc. + * Finally, it allocates physical memory using malloc() + * The function also updates worst-case heap size and worst-case RAM size + *--------------------------------------------------------------------*/ + +void *mem_alloc( + const char *func_name, + int func_lineno, + size_t size, + char *size_str /* the first char indicates m-alloc or c-alloc */ ) +{ + int index_record; + int32_t current_stack_size; + unsigned long hash; + allocator_record *ptr_record; + + if ( size == 0 ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Size of Zero not Supported" ); + exit( -1 ); + } + + /* Search for an existing record (that has been de-allocated before) */ + index_record = 0; + while ( ( ptr_record = get_mem_record( &hash, func_name, func_lineno, size_str, &index_record ) ) != NULL ) + { + if ( ptr_record->frame_allocated == -1 ) + { + break; + } + else + { + index_record++; + } + } + + /* Create new record */ + if ( ptr_record == NULL ) + { + if ( Num_Records >= Max_Num_Records ) + { + /* There is no room for a new record -> reallocate memory */ + Max_Num_Records += MAX_NUM_RECORDS_REALLOC_STEP; + allocation_list = realloc( allocation_list, Max_Num_Records * sizeof( allocator_record ) ); + } + + ptr_record = &( allocation_list[Num_Records] ); + + /* Initialize new record */ + ptr_record->hash = hash; + ptr_record->noccurances = 0; + ptr_record->total_block_size = 0; + ptr_record->total_used_size = 0; + ptr_record->frame_allocated = -1; + ptr_record->OOB_Flag = 0; + ptr_record->wc_heap_size_intra_frame = -1; + ptr_record->wc_heap_size_inter_frame = -1; + + index_record = Num_Records; + Num_Records++; + } + + /* Allocate memory block for the new record, add signature before the beginning and after the memory block and fill it with magic value */ + ptr_record->block_ptr = mem_alloc_block( size, size_str ); + + if ( ptr_record->block_ptr == NULL ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Cannot Allocate Memory!" ); + exit( -1 ); + } + + /* Save all auxiliary information about the memory block */ + strncpy( ptr_record->name, func_name, MAX_FUNCTION_NAME_LENGTH ); + ptr_record->name[MAX_FUNCTION_NAME_LENGTH] = '\0'; + strncpy( ptr_record->params, size_str, MAX_PARAMS_LENGTH ); /* Note: The size string starts with either 'm' or 'c' to indicate 'm'alloc or 'c'alloc */ + ptr_record->params[MAX_PARAMS_LENGTH] = '\0'; + ptr_record->lineno = func_lineno; + ptr_record->block_size = size; + ptr_record->total_block_size += size; + +#ifdef MEM_COUNT_DETAILS + /* Export heap memory allocation record to the .csv file */ + fprintf( fid_csv_filename, "A,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); +#endif + + if ( ptr_record->frame_allocated != -1 ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Attempt to Allocate the Same Memory Block with Freeing it First!" ); + exit( -1 ); + } + + ptr_record->frame_allocated = update_cnt; /* Store the current frame number -> later it will be used to determine the total duration */ + + /* Update Heap Size in the current frame */ + current_heap_size += ptr_record->block_size; + + /* Check, if this is the new Worst-Case RAM (stack + heap) */ + current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) ); + if ( current_stack_size + current_heap_size > wc_ram_size ) + { + wc_ram_size = current_stack_size + current_heap_size; + wc_ram_frame = update_cnt; + } + + /* Add new entry to the heap allocation call tree */ + if ( heap_allocation_call_tree == NULL ) + { + fprintf( stderr, "Error: Heap allocation call tree not created!" ); + exit( -1 ); + } + + /* check, if the maximum size of the call tree has been reached -> resize if so */ + if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size ) + { + heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP; + heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) ); + } + + /* push new entry (positive number means push op, neagtive number means pop op; zero index must be converted to 0.01 :-) */ + heap_allocation_call_tree[heap_allocation_call_tree_size++] = index_record; + + return ptr_record->block_ptr; +} + +/*-------------------------------------------------------------------* + * mem_alloc_block() + * + * Physical allocation of memory using malloc(). Appends 'signature' before and after the block, + * pre-fills memory block with magic value + *--------------------------------------------------------------------*/ + +static void *mem_alloc_block( size_t size, const char *size_str ) +{ + size_t rounded_size; + void *block_ptr; + char *tmp_ptr; + size_t n, f; + int32_t fill_value; + int32_t *ptr32; + int32_t mask, temp; + + /* Round Up Block Size */ + rounded_size = ROUND_BLOCK_SIZE( size ); + + /* Allocate memory using the standard malloc() by adding room for Signature Values */ + block_ptr = malloc( rounded_size + BLOCK_ROUNDING * 2 ); + + if ( block_ptr == NULL ) + { + return NULL; + } + + /* Add Signature Before the Start of the Block */ + ptr32 = (int32_t *) block_ptr; + n = N_32BITS_BLOCKS; + do + { + *ptr32++ = MAGIC_VALUE_OOB; + } while ( --n ); + + /* Fill Memory Block with Magic Value or 0 */ + fill_value = MAGIC_VALUE_USED; + if ( size_str[0] == 'c' ) + { + fill_value = 0x00000000; + } + n = size / sizeof( int32_t ); + while ( n-- ) + { + *ptr32++ = fill_value; + } + + /* Fill the Reminder of the Memory Block - After Rounding */ + n = rounded_size - size; + f = n % sizeof( int32_t ); + if ( f != 0 ) + { + /* when filling with '0' need to adapt the magic value */ + /* shift by [1->24, 2->16, 3->8] */ + mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); /* (1) */ + temp = MAGIC_VALUE_OOB & mask; + if ( fill_value != 0x0 ) + { /* for malloc merge fill value */ + temp += ( ~mask ) & MAGIC_VALUE_USED; + } /* for calloc the code in (1) above already introduces zeros */ + *ptr32++ = temp; + } + n /= sizeof( int32_t ); + n += N_32BITS_BLOCKS; + + /* Add Signature After the End of Block */ + do + { + *ptr32++ = MAGIC_VALUE_OOB; + } while ( --n ); + + /* Adjust the Memory Block Pointer (Magic Value Before and After the Memory Block Requested) */ + tmp_ptr = (char *) block_ptr; + tmp_ptr += BLOCK_ROUNDING; + block_ptr = (void *) tmp_ptr; + + return block_ptr; +} + +/*-------------------------------------------------------------------* + * mem_set_usage() + * + * Calculates actual usage of memory block by checking the magic value that was used to pre-fill + * each memory block during its allocation + *--------------------------------------------------------------------*/ + +static int mem_set_usage( allocator_record *record_ptr ) +{ + int total_bytes_used; + + size_t n; + int32_t *ptr32; + char *ptr8; + size_t total_bytes; + int32_t fill_value; + + fill_value = MAGIC_VALUE_USED; + if ( ( record_ptr->params[0] ) == 'c' ) + { + fill_value = 0x00000000; + } + + total_bytes = record_ptr->block_size; + + /* Check 4 bytes at a time */ + ptr32 = (int32_t *) record_ptr->block_ptr; + total_bytes_used = 0; + for ( n = total_bytes / sizeof( int32_t ); n > 0; n-- ) + { + if ( *ptr32++ != fill_value ) + { + total_bytes_used += sizeof( int32_t ); + } + } + + /* Check remaining bytes (If Applicable) 1 byte at a time */ + ptr8 = (char *) ptr32; + for ( n = total_bytes % sizeof( int32_t ); n > 0; n-- ) + { + if ( *ptr8++ != (char) fill_value ) + { + total_bytes_used++; + } + + /* Update Value */ + fill_value >>= 8; + } + + return total_bytes_used; +} + +/*-------------------------------------------------------------------* + * mem_check_OOB() + * + * Checks, if out-of-bounds access has occured. This is done by inspecting the 'signature' value + * taht has been added before and after the memory block during its allocation + *--------------------------------------------------------------------*/ + +static unsigned int mem_check_OOB( allocator_record *record_ptr ) +{ + int32_t *ptr32; + unsigned int OOB_Flag = 0x0; + int32_t mask; + size_t i; + int f; + + ptr32 = (int32_t *) record_ptr->block_ptr - N_32BITS_BLOCKS; + + /* Check the Signature at the Beginning of Memory Block */ + i = N_32BITS_BLOCKS; + do + { + if ( *ptr32++ ^ MAGIC_VALUE_OOB ) + { + OOB_Flag |= OOB_START; + } + } while ( --i ); + + /* Advance to End (Snap to lowest 32 Bits) */ + ptr32 += record_ptr->block_size / sizeof( int32_t ); + + /* Calculate Unused Space That has been added to get to the rounded Block Size */ + i = ROUND_BLOCK_SIZE( record_ptr->block_size ) - record_ptr->block_size; + + /* Partial Check of Signature at the End of Memory Block (for block size that has been rounded) */ + f = i % sizeof( int32_t ); + if ( f != 0 ) + { + mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); + if ( ( *ptr32++ ^ MAGIC_VALUE_OOB ) & mask ) + { + OOB_Flag |= OOB_END; + } + } + + /* Full Check of Signature at the End of Memory Block, i.e. all 32 Bits (for the remainder after rounding) */ + i /= sizeof( int32_t ); + i += N_32BITS_BLOCKS; + do + { + if ( *ptr32++ ^ MAGIC_VALUE_OOB ) + { + OOB_Flag |= OOB_END; + } + } while ( --i ); + + return OOB_Flag; +} + +/*-------------------------------------------------------------------* + * malloc_hash() + * + * Calculate hash from function name, line number and malloc size + *--------------------------------------------------------------------*/ + +static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str ) +{ + unsigned long hash = 5381; + const char *ptr_str; + + ptr_str = func_name; + while ( ptr_str != NULL && *ptr_str != '\0' ) + { + hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */ + } + + hash = ( ( hash << 5 ) + hash ) + func_lineno; /* hash * 33 + func_lineno */ + + ptr_str = size_str; + while ( ptr_str != NULL && *ptr_str != '\0' ) + { + hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */ + } + + return hash; +} + +/*-------------------------------------------------------------------* + * get_mem_record() + * + * Search for memory record in the internal list, return NULL if not found + * Start from index_record + *--------------------------------------------------------------------*/ + +allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record ) +{ + int i; + + if ( *index_record < 0 || *index_record > Num_Records ) + { + return NULL; + } + + /* calculate hash */ + *hash = malloc_hash( func_name, func_lineno, size_str ); + + for ( i = *index_record; i < Num_Records; i++ ) + { + /* check, if memory block is not allocated at the moment and the hash matches */ + if ( allocation_list[i].block_ptr == NULL && *hash == allocation_list[i].hash ) + { + *index_record = i; + return &( allocation_list[i] ); + } + } + + /* not found */ + *index_record = -1; + return NULL; +} + + +/*-------------------------------------------------------------------* + * mem_free() + * + * This function de-allocatesd the memory block and frees the mphysical memory with free(). + * It also updates actual and average usage of the memory block. + * + * Note: The record is not removed from the list and may be reused later on in mem_alloc()! + *--------------------------------------------------------------------*/ + +void mem_free( const char *func_name, int func_lineno, void *ptr ) +{ + int i, index_record; + char *tmp_ptr; + allocator_record *ptr_record; + + /* Search for the Block Pointer in the List */ + ptr_record = NULL; + index_record = -1; + for ( i = 0; i < Num_Records; i++ ) + { + if ( ptr == allocation_list[i].block_ptr ) + { /* Yes, Found it */ + ptr_record = &( allocation_list[i] ); + index_record = i; + break; + } + } + + if ( ptr_record == NULL ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", func_name, func_lineno, "Error: Unable to Find Record Corresponding to the Allocated Memory Block!" ); + exit( -1 ); + } + + /* Update the Heap Size */ + current_heap_size -= ptr_record->block_size; + + /* Calculate the Actual Usage of the Memory Block (Look for Signature) */ + ptr_record->total_used_size += mem_set_usage( ptr_record ); + + /* Check, if Out-Of-Bounds Access has been Detected */ + ptr_record->OOB_Flag = mem_check_OOB( ptr_record ); + +#ifdef MEM_COUNT_DETAILS + /* Export heap memory de-allocation record to the .csv file */ + fprintf( fid_csv_filename, "D,%d,%s,%d,%d\n", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size ); +#endif + + /* De-Allocate Memory Block */ + tmp_ptr = (char *) ptr; + tmp_ptr -= BLOCK_ROUNDING; + ptr = (void *) tmp_ptr; + free( ptr ); + + /* Add new entry to the heap allocation call tree */ + if ( heap_allocation_call_tree == NULL ) + { + fprintf( stderr, "Error: Heap allocation call tree not created!" ); + exit( -1 ); + } + + /* check, if the maximum size of the call tree has been reached -> resize if so */ + if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size ) + { + heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP; + heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) ); + } + + heap_allocation_call_tree[heap_allocation_call_tree_size++] = -index_record; + + /* Reset memory block pointer (this is checked when updating wc intra-frame and inter-frame memory) */ + ptr_record->block_ptr = NULL; + + return; +} + + +/*-------------------------------------------------------------------* + * update_mem() + * + * This function updates the worst-case intra-frame memory and the worst-case inter-frame memory. + *--------------------------------------------------------------------*/ + +void update_mem( void ) +{ + int i, j, flag_alloc = -1, i_record; + int size_current_intra_frame_heap; + int *list_current_intra_frame_heap = NULL, n_items_current_intra_frame_heap; + allocator_record *ptr_record; + + /* process the heap allocation call tree and prepare lists of intra-frame and inter-frame heap memory blocks for this frame */ + n_items_current_intra_frame_heap = 0; + size_current_intra_frame_heap = 0; + for ( i = 0; i < heap_allocation_call_tree_size; i++ ) + { + /* get the record */ + i_record = heap_allocation_call_tree[i]; + + if ( i_record > 0 ) + { + flag_alloc = 1; + } + else if ( i_record < 0 ) + { + flag_alloc = 0; + i_record = -i_record; + } + ptr_record = &( allocation_list[i_record] ); + + if ( ptr_record->frame_allocated == update_cnt && ptr_record->block_ptr == NULL ) + { + /* intra-frame heap memory */ + if ( list_current_intra_frame_heap == NULL ) + { + list_current_intra_frame_heap = (int *) malloc( heap_allocation_call_tree_size * sizeof( int ) ); + memset( list_current_intra_frame_heap, -1, heap_allocation_call_tree_size * sizeof( int ) ); + } + + /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ + if ( i_record == 0 ) + { + flag_alloc = 1; + for ( j = 0; j < n_items_current_intra_frame_heap; j++ ) + { + if ( list_current_intra_frame_heap[j] == i_record ) + { + flag_alloc = 0; + break; + } + } + } + + if ( flag_alloc ) + { + /* add to list */ + list_current_intra_frame_heap[n_items_current_intra_frame_heap++] = i_record; + size_current_intra_frame_heap += ptr_record->block_size; + + /* no need to re-size the list -> the initially allocated size should be large enough */ + } + else + { + /* remove from list */ + for ( j = 0; j < n_items_current_intra_frame_heap; j++ ) + { + if ( list_current_intra_frame_heap[j] == i_record ) + { + break; + } + } + memmove( &list_current_intra_frame_heap[j], &list_current_intra_frame_heap[j + 1], ( n_items_current_intra_frame_heap - j ) * sizeof( int ) ); + n_items_current_intra_frame_heap--; + size_current_intra_frame_heap -= ptr_record->block_size; + + /* reset block size */ + ptr_record->frame_allocated = -1; + ptr_record->block_size = 0; + } + } + else + { + /* inter-frame heap memory */ + + /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */ + if ( i_record == 0 ) + { + flag_alloc = 1; + for ( j = 0; j < n_items_current_inter_frame_heap; j++ ) + { + if ( list_current_inter_frame_heap[j] == i_record ) + { + flag_alloc = 0; + break; + } + } + } + + if ( flag_alloc ) + { + /* add to list */ + if ( n_items_current_inter_frame_heap >= max_items_current_inter_frame_heap ) + { + /* resize list, if needed */ + max_items_current_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; + list_current_inter_frame_heap = realloc( list_current_inter_frame_heap, max_items_current_inter_frame_heap * sizeof( int ) ); + } + + list_current_inter_frame_heap[n_items_current_inter_frame_heap++] = i_record; + size_current_inter_frame_heap += ptr_record->block_size; + } + else + { + /* remove from list */ + for ( j = 0; j < n_items_current_inter_frame_heap; j++ ) + { + if ( list_current_inter_frame_heap[j] == i_record ) + { + break; + } + } + memmove( &list_current_inter_frame_heap[j], &list_current_inter_frame_heap[j + 1], ( n_items_current_inter_frame_heap - j ) * sizeof( int ) ); + n_items_current_inter_frame_heap--; + size_current_inter_frame_heap -= ptr_record->block_size; + + /* reset block size */ + ptr_record->frame_allocated = -1; + ptr_record->block_size = 0; + } + } + } + + /* check, if this is the new worst-case for intra-frame heap memory */ + if ( size_current_intra_frame_heap > size_wc_intra_frame_heap ) + { + if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap ) + { + /* resize the list, if needed */ + max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; + list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) ); + } + + /* copy current-frame list to worst-case list */ + memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) ); + n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap; + size_wc_intra_frame_heap = size_current_intra_frame_heap; + location_wc_intra_frame_heap = update_cnt; + + /* update the wc numbers in all individual records */ + for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) + { + i_record = list_wc_intra_frame_heap[i]; + ptr_record = &( allocation_list[i_record] ); + ptr_record->wc_heap_size_intra_frame = ptr_record->block_size; + } + } + + /* check, if this is the new worst-case for inter-frame heap memory */ + if ( size_current_inter_frame_heap > size_wc_inter_frame_heap ) + { + if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap ) + { + /* resize list, if needed */ + max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP; + list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) ); + } + + /* copy current-frame list to worst-case list */ + memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) ); + n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap; + size_wc_inter_frame_heap = size_current_inter_frame_heap; + location_wc_inter_frame_heap = update_cnt; + + /* update the wc numbers in all individual records */ + for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) + { + i_record = list_wc_inter_frame_heap[i]; + ptr_record = &( allocation_list[i_record] ); + ptr_record->wc_heap_size_inter_frame = ptr_record->block_size; + } + } + + /* reset heap allocation call tree */ + heap_allocation_call_tree_size = 0; + + /* de-allocate list of intra-frame heap memory blocks in the current fraeme - it's needed only inside this function */ + if ( list_current_intra_frame_heap ) + { + free( list_current_intra_frame_heap ); + } + + return; +} + +#ifdef MEM_COUNT_DETAILS +/*-------------------------------------------------------------------* + * subst() + * + * Substitute character in string + *--------------------------------------------------------------------*/ + +static void subst( char *s, char from, char to ) +{ + while ( *s == from ) + { + *s++ = to; + } + + return; +} + + +/*-------------------------------------------------------------------* + * mem_count_summary() + * + * Print detailed (per-item) information about heap memory usage + *--------------------------------------------------------------------*/ + +static void mem_count_summary( void ) +{ + int i, j, index, index_record; + size_t length; + char buf[300], format_str[50], name_str[MAX_FUNCTION_NAME_LENGTH + 3], parms_str[MAX_PARAMS_LENGTH + 1], type_str[10], usage_str[20], size_str[20], line_str[10]; + allocator_record *ptr_record, *ptr; + + /* Prepare format string */ + sprintf( format_str, "%%-%ds %%5s %%6s %%-%ds %%20s %%6s ", MAX_FUNCTION_NAME_LENGTH, MAX_PARAMS_LENGTH ); + + if ( n_items_wc_intra_frame_heap > 0 ) + { + /* Intra-Frame Heap Size */ + fprintf( stdout, "\nList of memory blocks when maximum intra-frame heap size is reached:\n\n" ); + + /* Find duplicate records (same hash and worst-case heap size) */ + for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) + { + index_record = list_wc_intra_frame_heap[i]; + if ( index_record == -1 ) + { + continue; + } + + ptr_record = &( allocation_list[index_record] ); + for ( j = i + 1; j < n_items_wc_intra_frame_heap; j++ ) + { + index = list_wc_intra_frame_heap[j]; + if ( index == -1 ) + { + continue; + } + ptr = &( allocation_list[index] ); + + if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_intra_frame == ptr_record->wc_heap_size_intra_frame ) + { + ptr_record->noccurances++; + list_wc_intra_frame_heap[j] = -1; + } + } + } + + /* Print Header */ + sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Maximum Size", "Usage" ); + puts( buf ); + length = strlen( buf ); + sprintf( buf, "%0*d\n", (int) length - 1, 0 ); + subst( buf, '0', '-' ); + puts( buf ); + + for ( i = 0; i < n_items_wc_intra_frame_heap; i++ ) + { + index_record = list_wc_intra_frame_heap[i]; + + if ( index_record != -1 ) + { + /* get the record */ + ptr_record = &( allocation_list[index_record] ); + + /* prepare information strings */ + strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH ); + strcat( name_str, "()" ); + name_str[MAX_FUNCTION_NAME_LENGTH] = '\0'; + strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH ); + parms_str[MAX_PARAMS_LENGTH] = '\0'; + + if ( ptr_record->params[0] == 'm' ) + { + strcpy( type_str, "malloc" ); + } + else + { + strcpy( type_str, "calloc" ); + } + + sprintf( line_str, "%d", ptr_record->lineno ); + + /* prepare average usage & memory size strings */ + sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 1 ) ) * 100.0f ) ); + + if ( ptr_record->noccurances > 1 ) + { + sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + } + else + { + sprintf( size_str, "%d %s", (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + } + + sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str ); + puts( buf ); + } + } + + fprintf( stdout, "\n" ); + } + + if ( n_items_wc_inter_frame_heap > 0 ) + { + /* Inter-Frame Heap Size */ + fprintf( stdout, "\nList of memory blocks when maximum inter-frame heap size is reached:\n\n" ); + + /* Find duplicate records (same hash and worst-case heap size) */ + for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) + { + index_record = list_wc_inter_frame_heap[i]; + if ( index_record == -1 ) + { + continue; + } + ptr_record = &( allocation_list[index_record] ); + ptr_record->noccurances = 1; /* reset the counter as some blocks may have been both, intra-frame and inter-frame */ + for ( j = i + 1; j < n_items_wc_inter_frame_heap; j++ ) + { + index = list_wc_inter_frame_heap[j]; + if ( index == -1 ) + { + continue; + } + ptr = &( allocation_list[index] ); + + if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_inter_frame == ptr_record->wc_heap_size_inter_frame ) + { + ptr_record->noccurances++; + list_wc_inter_frame_heap[j] = -1; + } + } + } + + /* Print Header */ + sprintf( buf, format_str, "Function Name", "Line", "Type", "Function Parameters", "Memory Size", "Usage" ); + puts( buf ); + length = strlen( buf ); + sprintf( buf, "%0*d\n", (int) length - 1, 0 ); + subst( buf, '0', '-' ); + puts( buf ); + + for ( i = 0; i < n_items_wc_inter_frame_heap; i++ ) + { + index_record = list_wc_inter_frame_heap[i]; + + if ( index_record != -1 ) + { + /* get the record */ + ptr_record = &( allocation_list[index_record] ); + + /* prepare information strings */ + strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH ); + strcat( name_str, "()" ); + name_str[MAX_FUNCTION_NAME_LENGTH] = '\0'; + strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH ); + parms_str[MAX_PARAMS_LENGTH] = '\0'; + + if ( ptr_record->params[0] == 'm' ) + { + strcpy( type_str, "malloc" ); + } + else + { + strcpy( type_str, "calloc" ); + } + + sprintf( line_str, "%d", ptr_record->lineno ); + + /* prepare average usage & memory size strings */ + sprintf( usage_str, "%d%%", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 0.1f ) ) * 100.0f + 0.5f ) ); + + if ( ptr_record->noccurances > 1 ) + { + sprintf( size_str, "%dx%d %s", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + } + else + { + sprintf( size_str, "%d %s", (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] ); + } + + sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str ); + puts( buf ); + } + } + + fprintf( stdout, "\n" ); + } + + return; +} + +#endif + +/*-------------------------------------------------------------------* + * print_mem() + * + * Print information about ROM and RAM memory usage + *--------------------------------------------------------------------*/ + +void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) +{ + int i, nElem; + + fprintf( stdout, "\n\n --- Memory usage --- \n\n" ); + + if ( Const_Data_PROM_Table != NULL ) + { + nElem = 0; + while ( strcmp( Const_Data_PROM_Table[nElem].file_spec, "" ) != 0 ) + nElem++; + + for ( i = 0; i < nElem; i++ ) + { + fprintf( stdout, "Program ROM size (%s): %d instruction words\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size ); + } + + for ( i = 0; i < nElem; i++ ) + { + if ( Const_Data_PROM_Table[i].Get_Const_Data_Size_Func == NULL ) + { + fprintf( stdout, "Error: Cannot retrieve or calculate Table ROM size of (%s)!\n", Const_Data_PROM_Table[i].file_spec ); + } + + fprintf( stdout, "Table ROM (const data) size (%s): %d %s\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].Get_Const_Data_Size_Func() >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] ); + } + } + else + { + fprintf( stdout, "Program ROM size: not available\n" ); + fprintf( stdout, "Table ROM (const data) size: not available\n" ); + } + + if ( wc_ram_size > 0 ) + { + fprintf( stdout, "Maximum RAM (stack + heap) size: %d %s in frame %d\n", wc_ram_size >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], wc_ram_frame ); + } + else + { + fprintf( stdout, "Maximum RAM (stack + heap) size: not available\n" ); + } + + /* check, if the stack is empty */ + if ( ptr_current_stack != ptr_base_stack ) + { + fprintf( stderr, "Warning: Stack is not empty.\n" ); + } + + if ( ptr_base_stack - ptr_max_stack > 0 ) + { + fprintf( stdout, "Maximum stack size: %lu %s in frame %d\n", ( ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], + wc_stack_frame ); + } + else + { + fprintf( stdout, "Maximum stack size: not available\n" ); + } + + /* last update of intra-frame memory and inter-frame memory, if needed */ + if ( heap_allocation_call_tree_size > 0 ) + { + update_mem(); + } + + /* check, if all memory blocks have been deallocated (freed) */ + for ( i = 0; i < Num_Records; i++ ) + { + if ( allocation_list[i].block_ptr != NULL ) + { + fprintf( stderr, "Fct=%s, Ln=%i: %s!\n", allocation_list[i].name, allocation_list[i].lineno, "Error: Memory Block has not been De-Allocated with free()!" ); + exit( -1 ); + } + } + + if ( n_items_wc_intra_frame_heap > 0 ) + { + fprintf( stdout, "Maximum intra-frame heap size: %d %s in frame %d\n", size_wc_intra_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_intra_frame_heap ); + } + else + { + fprintf( stdout, "Maximum intra-frame heap size: 0\n" ); + } + + if ( n_items_wc_inter_frame_heap > 0 ) + { + fprintf( stdout, "Maximum inter-frame heap size: %d %s in frame %d\n", size_wc_inter_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_inter_frame_heap ); + } + else + { + fprintf( stdout, "Maximum inter-frame heap size: 0\n" ); + } + +#ifdef MEM_COUNT_DETAILS + /* Print detailed information about worst-case stack usage */ + if ( ptr_base_stack - ptr_max_stack > 0 ) + { + print_stack_call_tree(); + } + + /* Print detailed information about worst-case heap usage */ + mem_count_summary(); +#endif + + if ( Stat_Cnt_Size > 0 ) + { + fprintf( stdout, "\nNote: 1 word = %d bits\n", 8 << Stat_Cnt_Size ); + fprintf( stdout, "This is an optimistic estimate of memory consumption assuming that each variable type is stored with sizeof(type) bits\n" ); + } + + if ( n_items_wc_intra_frame_heap > 0 ) + { + fprintf( stdout, "Intra-frame heap memory is allocated and de-allocated in the same frame\n" ); + } + + /* De-allocate list of heap memory blocks */ + if ( allocation_list != NULL ) + { + free( allocation_list ); + } + + /* De-allocate heap allocation call tree */ + if ( heap_allocation_call_tree != NULL ) + { + free( heap_allocation_call_tree ); + } + + /* De-allocate intra-frame and inter-frame heap lists */ + if ( list_wc_intra_frame_heap != NULL ) + { + free( list_wc_intra_frame_heap ); + } + + if ( list_current_inter_frame_heap != NULL ) + { + free( list_current_inter_frame_heap ); + } + + if ( list_wc_inter_frame_heap != NULL ) + { + free( list_wc_inter_frame_heap ); + } + +#ifdef MEM_COUNT_DETAILS + if ( fid_csv_filename != NULL ) + { + fclose( fid_csv_filename ); + } +#endif + + return; +} + +#endif /* WMOPS */ + +#ifndef WMOPS +int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */ +#endif + + diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index 4d7eee8275d3d876b1acd9d25bbe7d38d98bafb8..767a02dc6dfef1c23d0bf774511f53b4cafa3891 100644 GIT binary patch literal 42697 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 literal 43846 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 mZYo}#69leU1>b9JGzXDyVQXzY+cap8RX\r\n" "#endif\r\n" "\r\n" - "#include \"wmc_auto.h\"\r\n" "\r\n" -"\r\n" "#define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */\r\n" "\r\n" "#ifdef WMOPS\r\n" @@ -37,23 +35,26 @@ " * Complexity counting tool\r\n" " *--------------------------------------------------------------------*/\r\n" "\r\n" -"#define MAX_RECORDS 1024\r\n" -"#define MAX_CHAR 64\r\n" -"#define MAX_STACK 64\r\n" -"#define DOUBLE_MAX 0x80000000\r\n" +"#define MAX_FUNCTION_NAME_LENGTH 50 /* Maximum length of the function name */\r\n" +"#define MAX_PARAMS_LENGTH 50 /* Maximum length of the function parameter string */\r\n" +"#define MAX_NUM_RECORDS 300 /* Initial maximum number of records -> mightb be increased during runtime, if needed */\r\n" +"#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of records, increase the number of records by this number */\r\n" +"\r\n" +"#define MAX_CALL_TREE_DEPTH 100 /* maximum depth of the function call tree */\r\n" +"#define DOUBLE_MAX 0x80000000\r\n" "\r\n" -"struct wmops_record\r\n" +"typedef struct \r\n" "{\r\n" -" char label[MAX_CHAR];\r\n" +" char label[MAX_FUNCTION_NAME_LENGTH];\r\n" " long call_number;\r\n" " long update_cnt;\r\n" -" int call_tree[MAX_RECORDS];\r\n" +" int call_tree[MAX_CALL_TREE_DEPTH];\r\n" " double start_selfcnt;\r\n" " double current_selfcnt;\r\n" " double max_selfcnt;\r\n" " double min_selfcnt;\r\n" " double tot_selfcnt;\r\n" -" double start_cnt; /* The following take into account the decendants */\r\n" +" double start_cnt; \r\n" " double current_cnt;\r\n" " double max_cnt;\r\n" " double min_cnt;\r\n" @@ -64,16 +65,14 @@ " double wc_selfcnt;\r\n" " int32_t wc_call_number;\r\n" "#endif\r\n" -"};\r\n" +"} wmops_record;\r\n" "\r\n" "double ops_cnt;\r\n" "double prom_cnt;\r\n" "double inst_cnt[NUM_INST];\r\n" "\r\n" -"static struct wmops_record wmops[MAX_RECORDS];\r\n" -"static int stack[MAX_STACK];\r\n" -"static int sptr;\r\n" -"static int num_records;\r\n" +"static wmops_record *wmops = NULL;\r\n" +"static int num_wmops_records, max_num_wmops_records;\r\n" "static int current_record;\r\n" "static long update_cnt;\r\n" "static double start_cnt;\r\n" @@ -81,20 +80,41 @@ "static double min_cnt;\r\n" "static double inst_cnt_wc[NUM_INST];\r\n" "static long fnum_cnt_wc;\r\n" -"\r\n" +"static int *wmops_caller_stack = NULL, wmops_caller_stack_index, max_wmops_caller_stack_index = 0;\r\n" "static int *heap_allocation_call_tree = NULL, heap_allocation_call_tree_size = 0, heap_allocation_call_tree_max_size = 0;\r\n" "\r\n" -"\r\n" "void reset_wmops( void )\r\n" "{\r\n" " int i, j;\r\n" "\r\n" -" for ( i = 0; i < MAX_RECORDS; i++ )\r\n" +" num_wmops_records = 0;\r\n" +" max_num_wmops_records = MAX_NUM_RECORDS;\r\n" +" current_record = -1;\r\n" +" update_cnt = 0;\r\n" +"\r\n" +" max_cnt = 0.0;\r\n" +" min_cnt = DOUBLE_MAX;\r\n" +" start_cnt = 0.0;\r\n" +" ops_cnt = 0.0;\r\n" +"\r\n" +" /* allocate the list of wmops records */\r\n" +" if ( wmops == NULL )\r\n" +" {\r\n" +" wmops = (wmops_record *)malloc( max_num_wmops_records * sizeof( wmops_record ) );\r\n" +" }\r\n" +"\r\n" +" if ( wmops == NULL )\r\n" +" {\r\n" +" fprintf( stderr, \"Error: Unable to Allocate List of WMOPS Records!\" );\r\n" +" exit( -1 );\r\n" +" }\r\n" +"\r\n" +" for ( i = 0; i < max_num_wmops_records; i++ )\r\n" " {\r\n" " strcpy( &wmops[i].label[0], \"\\0\" );\r\n" " wmops[i].call_number = 0;\r\n" " wmops[i].update_cnt = 0;\r\n" -" for ( j = 0; j < MAX_RECORDS; j++ )\r\n" +" for ( j = 0; j < MAX_CALL_TREE_DEPTH; j++ )\r\n" " {\r\n" " wmops[i].call_tree[j] = -1;\r\n" " }\r\n" @@ -112,22 +132,30 @@ " wmops[i].wc_cnt = 0.0;\r\n" " wmops[i].wc_selfcnt = 0.0;\r\n" " wmops[i].current_call_number = 0;\r\n" +" wmops[i].wc_call_number = -1;\r\n" "#endif\r\n" " }\r\n" "\r\n" -" for ( i = 0; i < MAX_STACK; i++ )\r\n" +" /* allocate the list of wmops callers to track the sequence of function calls */\r\n" +" wmops_caller_stack_index = 0;\r\n" +" max_wmops_caller_stack_index = MAX_NUM_RECORDS;\r\n" +" if ( wmops_caller_stack == NULL )\r\n" " {\r\n" -" stack[i] = -1;\r\n" +" wmops_caller_stack = malloc( max_wmops_caller_stack_index * sizeof( int ) );\r\n" " }\r\n" -" sptr = 0;\r\n" -" num_records = 0;\r\n" -" current_record = -1;\r\n" -" update_cnt = 0;\r\n" "\r\n" -" max_cnt = 0.0;\r\n" -" min_cnt = DOUBLE_MAX;\r\n" -" start_cnt = 0.0;\r\n" -" ops_cnt = 0.0;\r\n" +" if ( wmops_caller_stack == NULL )\r\n" +" {\r\n" +" fprintf( stderr, \"Error: Unable to Allocate List of WMOPS Callers!\" );\r\n" +" exit( -1 );\r\n" +" }\r\n" +"\r\n" +" for ( i = 0; i < max_wmops_caller_stack_index; i++ )\r\n" +" {\r\n" +" wmops_caller_stack[i] = -1;\r\n" +" }\r\n" +"\r\n" +" return;\r\n" "}\r\n" "\r\n" "\r\n" @@ -136,9 +164,9 @@ " int new_flag;\r\n" " int i, j;\r\n" "\r\n" -" /* Check if new function record label */\r\n" +" /* Check, if this is a new function label */\r\n" " new_flag = 1;\r\n" -" for ( i = 0; i < num_records; i++ )\r\n" +" for ( i = 0; i < num_wmops_records; i++ )\r\n" " {\r\n" " if ( strcmp( wmops[i].label, label ) == 0 )\r\n" " {\r\n" @@ -147,33 +175,36 @@ " }\r\n" " }\r\n" "\r\n" -" /* Configure new record */\r\n" +" /* Create a new record in the list */\r\n" " if ( new_flag )\r\n" " {\r\n" -" if ( num_records >= MAX_RECORDS )\r\n" +" if ( num_wmops_records >= max_num_wmops_records )\r\n" " {\r\n" -" fprintf( stdout, \"push_wmops(): exceeded MAX_RECORDS count.\\n\\n\" );\r\n" -" exit( -1 );\r\n" +" /* There is no room for a new wmops record -> reallocate the list */\r\n" +" max_num_wmops_records += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" +" wmops = realloc( wmops, max_num_wmops_records * sizeof( wmops_record ) );\r\n" " }\r\n" +"\r\n" " strcpy( wmops[i].label, label );\r\n" -" num_records++;\r\n" +" num_wmops_records++;\r\n" " }\r\n" "\r\n" -" /* Push current context onto stack */\r\n" +" /* Push the current context info to the new record */\r\n" " if ( current_record >= 0 )\r\n" " {\r\n" -" if ( sptr >= MAX_STACK )\r\n" +" if ( wmops_caller_stack_index >= max_wmops_caller_stack_index )\r\n" " {\r\n" -" fprintf( stdout, \"\\r push_wmops(): stack exceeded, try inreasing MAX_STACK\\n\" );\r\n" -" exit( -1 );\r\n" +" /* There is no room for a new record -> reallocate the list */\r\n" +" max_wmops_caller_stack_index += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" +" wmops_caller_stack = realloc( wmops_caller_stack, max_wmops_caller_stack_index * sizeof( int ) );\r\n" " }\r\n" -" stack[sptr++] = current_record;\r\n" +" wmops_caller_stack[wmops_caller_stack_index++] = current_record;\r\n" "\r\n" " /* accumulate op counts */\r\n" " wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt;\r\n" "\r\n" " /* update call tree */\r\n" -" for ( j = 0; j < MAX_RECORDS; j++ )\r\n" +" for ( j = 0; j < MAX_CALL_TREE_DEPTH; j++ )\r\n" " {\r\n" " if ( wmops[i].call_tree[j] == current_record )\r\n" " {\r\n" @@ -187,7 +218,7 @@ " }\r\n" " }\r\n" "\r\n" -" /* init current record */\r\n" +" /* update the current context info */\r\n" " current_record = i;\r\n" " wmops[current_record].start_selfcnt = ops_cnt;\r\n" " wmops[current_record].start_cnt = ops_cnt;\r\n" @@ -215,9 +246,9 @@ " wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt;\r\n" "\r\n" " /* Get back previous context from stack */\r\n" -" if ( sptr > 0 )\r\n" +" if ( wmops_caller_stack_index > 0 )\r\n" " {\r\n" -" current_record = stack[--sptr];\r\n" +" current_record = wmops_caller_stack[--wmops_caller_stack_index];\r\n" " wmops[current_record].start_selfcnt = ops_cnt;\r\n" " }\r\n" " else\r\n" @@ -239,9 +270,9 @@ " float tmpF;\r\n" "#endif\r\n" "\r\n" -" if ( sptr != 0 )\r\n" +" if ( wmops_caller_stack_index != 0 )\r\n" " {\r\n" -" fprintf( stdout, \"update_wmops(): Stack must be empty!\\n\" );\r\n" +" fprintf( stdout, \"update_wmops(): WMOPS caller stack corrupted - check that all push_wmops() are matched with pop_wmops()!\\n\" );\r\n" " exit( -1 );\r\n" " }\r\n" "\r\n" @@ -266,7 +297,7 @@ "#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" " if ( ops_cnt - start_cnt > max_cnt )\r\n" " {\r\n" -" for ( i = 0; i < num_records; i++ )\r\n" +" for ( i = 0; i < num_wmops_records; i++ )\r\n" " {\r\n" " wmops[i].wc_cnt = wmops[i].current_cnt;\r\n" " wmops[i].wc_selfcnt = wmops[i].current_selfcnt;\r\n" @@ -275,7 +306,7 @@ " }\r\n" "#endif\r\n" "\r\n" -" for ( i = 0; i < num_records; i++ )\r\n" +" for ( i = 0; i < num_wmops_records; i++ )\r\n" " {\r\n" " wmops[i].tot_selfcnt += wmops[i].current_selfcnt;\r\n" " wmops[i].tot_cnt += wmops[i].current_cnt;\r\n" @@ -365,7 +396,7 @@ "\r\n" " /* calculate maximum label length for compact prinout */\r\n" " max_label_len = 0;\r\n" -" for ( i = 0; i < num_records; i++ )\r\n" +" for ( i = 0; i < num_wmops_records; i++ )\r\n" " {\r\n" " label_len = strlen( wmops[i].label );\r\n" " if ( label_len > max_label_len )\r\n" @@ -381,7 +412,7 @@ " fprintf( stdout, sfmt, max_label_len, \" routine\", \" calls\", \" min \", \" max \", \" avg \", \" min \", \" max \", \" avg \" );\r\n" " fprintf( stdout, sfmt, max_label_len, \"---------------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\" );\r\n" "\r\n" -" for ( i = 0; i < num_records; i++ )\r\n" +" for ( i = 0; i < num_wmops_records; i++ )\r\n" " {\r\n" " fprintf( stdout, dfmt, max_label_len, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt,\r\n" " wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt,\r\n" @@ -397,37 +428,42 @@ " fprintf( stdout, \"\\n\" );\r\n" "\r\n" "#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" fprintf( stdout, \"\\nComplexity analysis for the worst-case frame %ld:\\n\", fnum_cnt_wc );\r\n" +" fprintf( stdout, \"\\nComplexity analysis for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc );\r\n" " fprintf( stdout, \"%*s %8s %10s %12s\\n\", max_label_len, \" routine\", \" calls\", \" SELF\", \" CUMULATIVE\" );\r\n" " fprintf( stdout, \"%*s %8s %10s %10s\\n\", max_label_len, \"---------------\", \"------\", \"------\", \"----------\" );\r\n" "\r\n" -" for ( i = 0; i < num_records; i++ )\r\n" +" for ( i = 0; i < num_wmops_records; i++ )\r\n" " {\r\n" -" fprintf( stdout, \"%*s %8d %10.3f %12.3f\\n\", max_label_len, wmops[i].label, wmops[i].wc_call_number, FAC * wmops[i].wc_selfcnt, FAC * wmops[i].wc_cnt );\r\n" +" if ( wmops[i].wc_call_number > 0 )\r\n" +" {\r\n" +" fprintf( stdout, \"%*s %8d %10.3f %12.3f\\n\", max_label_len, wmops[i].label, wmops[i].wc_call_number, FAC * wmops[i].wc_selfcnt, FAC * wmops[i].wc_cnt );\r\n" +" }\r\n" " }\r\n" "\r\n" -" fprintf( stdout, \"\\nCall Tree:\\n\\n\" );\r\n" -" fprintf( stdout, sfmtt, \" function\", \"num\", \"called by: \" );\r\n" +" fprintf( stdout, \"\\nCall tree for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc );\r\n" +" fprintf( stdout, sfmtt, \" function\", \"num\", \"called by \" );\r\n" " fprintf( stdout, sfmtt, \"---------------\", \"---\", \"--------------\" );\r\n" "\r\n" -" for ( i = 0; i < num_records; i++ )\r\n" +" for ( i = 0; i < num_wmops_records; i++ )\r\n" " {\r\n" -" fprintf( stdout, dfmtt, wmops[i].label, i );\r\n" -" for ( j = 0; wmops[i].call_tree[j] != -1; j++ )\r\n" +" if ( wmops[i].wc_call_number > 0 )\r\n" " {\r\n" -" if ( j != 0 )\r\n" +" fprintf( stdout, dfmtt, wmops[i].label, i );\r\n" +" for ( j = 0; wmops[i].call_tree[j] != -1 && j < MAX_CALL_TREE_DEPTH; j++ )\r\n" " {\r\n" -" fprintf( stdout, \", \" );\r\n" +" if ( j != 0 )\r\n" +" {\r\n" +" fprintf( stdout, \", \" );\r\n" +" }\r\n" +" fprintf( stdout, \"%d\", wmops[i].call_tree[j] );\r\n" " }\r\n" -" fprintf( stdout, \"%d\", wmops[i].call_tree[j] );\r\n" +" fprintf( stdout, \"\\n\" );\r\n" " }\r\n" -" fprintf( stdout, \"\\n\" );\r\n" " }\r\n" "\r\n" -" fprintf( stdout, sfmtt, \"---------------\", \"---\", \"--------------\" );\r\n" " fprintf( stdout, \"\\n\\n\" );\r\n" "\r\n" -" fprintf( stdout, \"\\nInstruction type analysis for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc ); /* added -- JPA */\r\n" +" fprintf( stdout, \"\\nInstruction type analysis for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc ); \r\n" " for ( i = 0; i < NUM_INST; i++ )\r\n" " {\r\n" " switch ( (enum instructions) i )\r\n" @@ -524,12 +560,6 @@ " * #define WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP macro pair around the malloc(), calloc() and free().\r\n" " *--------------------------------------------------------------------*/\r\n" "\r\n" -"#define MAX_RECORDABLE_CALLS 100\r\n" -"#define MAX_FUNCTION_NAME_LENGTH 35 /* Maximum length that the function string will be truncated to */\r\n" -"#define MAX_PARAMS_LENGTH 50 /* Maximum length that the parameter string will be truncated to */\r\n" -"#define MAX_NUM_RECORDS 300 /* Initial maximum number of memory records -> mightb be increased during runtime, if needed */\r\n" -"#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of memory records, increase the number of records by this number */\r\n" -"\r\n" "/* This is the value (in bytes) towards which the block size is rounded. For example, a block of 123 bytes, when using\r\n" " a 32 bits system, will end up taking 124 bytes since the last unused byte cannot be used for another block. */\r\n" "#ifdef MEM_ALIGN_64BITS\r\n" @@ -539,15 +569,13 @@ "#endif\r\n" "\r\n" "#define N_32BITS_BLOCKS ( BLOCK_ROUNDING / sizeof( int32_t ) )\r\n" +"#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) )\r\n" "\r\n" "#define MAGIC_VALUE_OOB 0x12A534F0 /* Signature value which is inserted before and after each allocated memory block, used to detect out-of-bound access */\r\n" "#define MAGIC_VALUE_USED ( ~MAGIC_VALUE_OOB ) /* Value used to pre-fill allocated memory blocks, used to calculate actual memory usage */\r\n" "#define OOB_START 0x1 /* Flag indicating out-of-bounds access before memory block */\r\n" "#define OOB_END 0x2 /* Flag indicating out-of-bounds access after memory block */\r\n" "\r\n" -"#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) )\r\n" -"#define IS_CALLOC( str ) ( str[0] == 'c' )\r\n" -"\r\n" "#ifdef MEM_COUNT_DETAILS\r\n" "const char *csv_filename = \"mem_analysis.csv\";\r\n" "static FILE *fid_csv_filename = NULL;\r\n" @@ -559,8 +587,16 @@ " int16_t *stack_ptr;\r\n" "} caller_info;\r\n" "\r\n" -"caller_info stack_callers[2][MAX_RECORDABLE_CALLS];\r\n" +"static caller_info *stack_callers[2] = {NULL, NULL};\r\n" "\r\n" +"static int16_t *ptr_base_stack = 0; /* Pointer to the bottom of stack (base pointer). Stack grows up. */\r\n" +"static int16_t *ptr_current_stack = 0; /* Pointer to the current stack pointer */\r\n" +"static int16_t *ptr_max_stack = 0; /* Pointer to the maximum stack pointer (the farest point from the bottom of stack) */\r\n" +"static int32_t wc_stack_frame = 0; /* Frame corresponding to the worst-case stack usage */\r\n" +"static int current_calls = 0, max_num_calls = MAX_NUM_RECORDS;\r\n" +"static char location_max_stack[256] = \"undefined\";\r\n" +"\r\n" +"/* Heap-related variables */\r\n" "typedef struct\r\n" "{\r\n" " char name[MAX_FUNCTION_NAME_LENGTH + 1]; /* +1 for NUL */\r\n" @@ -580,18 +616,12 @@ "\r\n" "allocator_record *allocation_list = NULL;\r\n" "\r\n" -"static int16_t *ptr_base_stack = 0; /* Pointer to the bottom of stack (base pointer). Stack grows up. */\r\n" -"static int16_t *ptr_current_stack = 0; /* Pointer to the current stack pointer */\r\n" -"static int16_t *ptr_max_stack = 0; /* Pointer to the maximum stack pointer (the farest point from the bottom of stack) */\r\n" -"static int32_t wc_stack_frame = 0; /* Frame corresponding to the worst-case stack usage */\r\n" -"static int32_t wc_ram_size, wc_ram_frame;\r\n" -"static int32_t current_heap_size;\r\n" -"static int current_calls = 0;\r\n" -"static char location_max_stack[256] = \"undefined\";\r\n" "static int Num_Records, Max_Num_Records;\r\n" "static size_t Stat_Cnt_Size = USE_BYTES;\r\n" "static const char *Count_Unit[] = { \"bytes\", \"words\", \"words\" };\r\n" "\r\n" +"static int32_t wc_ram_size, wc_ram_frame;\r\n" +"static int32_t current_heap_size;\r\n" "static int *list_wc_intra_frame_heap, n_items_wc_intra_frame_heap, max_items_wc_intra_frame_heap, size_wc_intra_frame_heap, location_wc_intra_frame_heap;\r\n" "static int *list_current_inter_frame_heap, n_items_current_inter_frame_heap, max_items_current_inter_frame_heap, size_current_inter_frame_heap;\r\n" "static int *list_wc_inter_frame_heap, n_items_wc_inter_frame_heap, max_items_wc_inter_frame_heap, size_wc_inter_frame_heap, location_wc_inter_frame_heap;\r\n" @@ -612,11 +642,28 @@ " int16_t something;\r\n" " size_t tmp_size;\r\n" "\r\n" +" /* initialize list of stack records */\r\n" +" if ( stack_callers[0] == NULL )\r\n" +" {\r\n" +" stack_callers[0] = malloc( MAX_NUM_RECORDS * sizeof( caller_info ) );\r\n" +" stack_callers[1] = malloc( MAX_NUM_RECORDS * sizeof( caller_info ) );\r\n" +" }\r\n" +"\r\n" +" if ( stack_callers[0] == NULL || stack_callers[1] == NULL )\r\n" +" {\r\n" +" fprintf( stderr, \"Error: Unable to Allocate List of Stack Records!\" );\r\n" +" exit( -1 );\r\n" +" }\r\n" +"\r\n" +" current_calls = 0;\r\n" +" max_num_calls = MAX_NUM_RECORDS;\r\n" +"\r\n" " /* initialize stack pointers */\r\n" " ptr_base_stack = &something;\r\n" " ptr_max_stack = ptr_base_stack;\r\n" " ptr_current_stack = ptr_base_stack;\r\n" "\r\n" +" /* initialize the unit of memory block size */\r\n" " Stat_Cnt_Size = cnt_size;\r\n" "\r\n" " /* Check, if sizeof(int32_t) is 4 bytes */\r\n" @@ -742,11 +789,12 @@ "\r\n" " (void) *filename; /* to avoid compilation warning */\r\n" "\r\n" -" /* Is there room to save the caller's information? */\r\n" -" if ( current_calls >= MAX_RECORDABLE_CALLS )\r\n" -" { /* No */\r\n" -" fprintf( stderr, \"No more room to store call stack info. Please increase MAX_RECORDABLE_CALLS\" );\r\n" -" exit( -1 );\r\n" +" if ( current_calls >= max_num_calls )\r\n" +" {\r\n" +" /* There is no room for a new record -> reallocate the list */\r\n" +" max_num_calls += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" +" stack_callers[0] = realloc( stack_callers[0], max_num_calls * sizeof( caller_info ) );\r\n" +" stack_callers[1] = realloc( stack_callers[1], max_num_calls * sizeof( caller_info ) );\r\n" " }\r\n" "\r\n" " /* Valid Function Name? */\r\n" @@ -763,7 +811,7 @@ " /* Save the Stack Pointer */\r\n" " stack_callers[0][current_calls].stack_ptr = ptr_current_stack;\r\n" "\r\n" -" /* Increase Stack Calling Tree Level */\r\n" +" /* Increase the Number of Calls in the List */\r\n" " current_calls++;\r\n" "\r\n" " /* Is this the First Time or the Worst Case? */\r\n" @@ -772,15 +820,17 @@ " /* Save Info about it */\r\n" " ptr_max_stack = ptr_current_stack;\r\n" "\r\n" -" wc_stack_frame = update_cnt; /* current frame number is stored in the variable update_cnt and updated in the function update_wmops() */\r\n" +" /* save the worst-case frame number */\r\n" +" /* current frame number is stored in the variable update_cnt and updated in the function update_wmops() */\r\n" +" wc_stack_frame = update_cnt; \r\n" " strncpy( location_max_stack, fctname, sizeof( location_max_stack ) - 1 );\r\n" " location_max_stack[sizeof( location_max_stack ) - 1] = '\\0';\r\n" "\r\n" " /* Save Call Tree */\r\n" " memmove( stack_callers[1], stack_callers[0], sizeof( caller_info ) * current_calls );\r\n" "\r\n" -" /* Terminate the List (Unless Full) */\r\n" -" if ( current_calls < MAX_RECORDABLE_CALLS )\r\n" +" /* Terminate the List with 0 (for printing purposes) */\r\n" +" if ( current_calls < max_num_calls )\r\n" " {\r\n" " stack_callers[1][current_calls].function_name[0] = 0;\r\n" " }\r\n" @@ -816,13 +866,13 @@ "\r\n" " (void) *filename; /* to avoid compilation warning */\r\n" "\r\n" -" /* Decrease Stack Calling */\r\n" +" /* Decrease the Number of Records */\r\n" " current_calls--;\r\n" "\r\n" " /* Get Pointer to Caller Information */\r\n" " caller_info_ptr = &stack_callers[0][current_calls];\r\n" "\r\n" -" /* Check, if Names Match */\r\n" +" /* Check, if the Function Names Match */\r\n" " if ( strncmp( caller_info_ptr->function_name, fctname, MAX_FUNCTION_NAME_LENGTH ) != 0 )\r\n" " {\r\n" " fprintf( stderr, \"Invalid usage of pop_stack()\" );\r\n" @@ -861,7 +911,7 @@ " fprintf( stdout, \"\\nList of functions when maximum stack size is reached:\\n\\n\" );\r\n" "\r\n" " caller_info_ptr = &stack_callers[1][0];\r\n" -" for ( call_level = 0; call_level < MAX_RECORDABLE_CALLS; call_level++ )\r\n" +" for ( call_level = 0; call_level < max_num_calls; call_level++ )\r\n" " {\r\n" " /* Done? */\r\n" " if ( caller_info_ptr->function_name[0] == 0 )\r\n" @@ -1060,7 +1110,7 @@ "\r\n" " /* Fill Memory Block with Magic Value or 0 */\r\n" " fill_value = MAGIC_VALUE_USED;\r\n" -" if ( IS_CALLOC( size_str ) )\r\n" +" if ( size_str[0] == 'c' )\r\n" " {\r\n" " fill_value = 0x00000000;\r\n" " }\r\n" @@ -1924,4 +1974,5 @@ "#ifndef WMOPS\r\n" "int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */\r\n" "#endif\r\n" +"\r\n" "\r\n" \ No newline at end of file diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index cfd4a71a..938ba8f4 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -30,11 +30,9 @@ "#endif\r\n" "\r\n" "/* Real-time relationships */\r\n" -"#define FRAMES_PER_SECOND 50.0\r\n" -"#define MILLION_CYCLES 1e6\r\n" +"#define FRAMES_PER_SECOND 50.0 \r\n" "#define WMOPS_BOOST_FAC ( 1.0f ) /* scaling factor for equalizing the difference between automatic and manual instrumentation */\r\n" -"#define FAC ( FRAMES_PER_SECOND / MILLION_CYCLES * WMOPS_BOOST_FAC )\r\n" -"#define NUM_INST 20 /* Total number of instruction types (in enum below) */\r\n" +"#define FAC ( FRAMES_PER_SECOND / 1e6 * WMOPS_BOOST_FAC )\r\n" "\r\n" "\r\n" "#ifdef WMOPS\r\n" @@ -59,7 +57,8 @@ " _TEST,\r\n" " _POWER,\r\n" " _LOG,\r\n" -" _MISC\r\n" +" _MISC,\r\n" +" NUM_INST\r\n" "};\r\n" "\r\n" "#define _ADD_C 1\r\n" From cd23bfd6e2925539d311941085164120ae14b428 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 27 Sep 2023 09:22:34 +0200 Subject: [PATCH 22/53] support for counting BASOP complexity --- src/wmc_tool/wmc_auto_c.txt | 171 ++++++++++++++- src/wmc_tool/wmc_auto_h.txt | 406 +++++++++++++++++++++++++++++++++++- 2 files changed, 574 insertions(+), 3 deletions(-) diff --git a/src/wmc_tool/wmc_auto_c.txt b/src/wmc_tool/wmc_auto_c.txt index 142e8cd5..62659403 100644 --- a/src/wmc_tool/wmc_auto_c.txt +++ b/src/wmc_tool/wmc_auto_c.txt @@ -39,7 +39,6 @@ "#define MAX_PARAMS_LENGTH 50 /* Maximum length of the function parameter string */\r\n" "#define MAX_NUM_RECORDS 300 /* Initial maximum number of records -> mightb be increased during runtime, if needed */\r\n" "#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of records, increase the number of records by this number */\r\n" -"\r\n" "#define MAX_CALL_TREE_DEPTH 100 /* maximum depth of the function call tree */\r\n" "#define DOUBLE_MAX 0x80000000\r\n" "\r\n" @@ -49,6 +48,7 @@ " long call_number;\r\n" " long update_cnt;\r\n" " int call_tree[MAX_CALL_TREE_DEPTH];\r\n" +" long LastWOper;\r\n" " double start_selfcnt;\r\n" " double current_selfcnt;\r\n" " double max_selfcnt;\r\n" @@ -86,6 +86,7 @@ "void reset_wmops( void )\r\n" "{\r\n" " int i, j;\r\n" +" unsigned int *ptr;\r\n" "\r\n" " num_wmops_records = 0;\r\n" " max_num_wmops_records = MAX_NUM_RECORDS;\r\n" @@ -109,6 +110,20 @@ " exit( -1 );\r\n" " }\r\n" "\r\n" +" /* allocate the BASOP WMOPS counter */\r\n" +" if ( multiCounter == NULL )\r\n" +" {\r\n" +" multiCounter = (BASIC_OP *) malloc( max_num_wmops_records * sizeof( BASIC_OP ) );\r\n" +" }\r\n" +"\r\n" +" if ( multiCounter == NULL )\r\n" +" {\r\n" +" fprintf( stderr, \"Error: Unable to Allocate the BASOP WMOPS counter!\" );\r\n" +" exit( -1 );\r\n" +" }\r\n" +"\r\n" +" /* initilize the list of wmops records */\r\n" +" /* initilize the BASOP WMOPS counters */\r\n" " for ( i = 0; i < max_num_wmops_records; i++ )\r\n" " {\r\n" " strcpy( &wmops[i].label[0], \"\\0\" );\r\n" @@ -134,6 +149,14 @@ " wmops[i].current_call_number = 0;\r\n" " wmops[i].wc_call_number = -1;\r\n" "#endif\r\n" +"\r\n" +" /* clear all BASOP operation counters */\r\n" +" ptr = (unsigned int*) &multiCounter[i];\r\n" +" for ( j = 0; j < (int) ( sizeof(BASIC_OP ) / sizeof( unsigned int ) ); j++ )\r\n" +" {\r\n" +" *ptr++ = 0;\r\n" +" }\r\n" +" wmops[i].LastWOper = 0;\r\n" " }\r\n" "\r\n" " /* allocate the list of wmops callers to track the sequence of function calls */\r\n" @@ -155,6 +178,10 @@ " wmops_caller_stack[i] = -1;\r\n" " }\r\n" "\r\n" +" /* initialize auxiliary BASOP WMOPS variables */\r\n" +" call_occurred = 1;\r\n" +" funcId_where_last_call_to_else_occurred = INT_MAX;\r\n" +"\r\n" " return;\r\n" "}\r\n" "\r\n" @@ -183,9 +210,11 @@ " /* There is no room for a new wmops record -> reallocate the list */\r\n" " max_num_wmops_records += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" " wmops = realloc( wmops, max_num_wmops_records * sizeof( wmops_record ) );\r\n" +" multiCounter = realloc( multiCounter, max_num_wmops_records * sizeof( BASIC_OP ) );\r\n" " }\r\n" "\r\n" " strcpy( wmops[i].label, label );\r\n" +"\r\n" " num_wmops_records++;\r\n" " }\r\n" "\r\n" @@ -227,12 +256,16 @@ " wmops[current_record].current_call_number++;\r\n" "#endif\r\n" "\r\n" +" /* set the ID of BASOP functions counters */\r\n" +" Set_BASOP_WMOPS_counter( current_record );\r\n" +"\r\n" " return;\r\n" "}\r\n" "\r\n" "\r\n" "void pop_wmops( void )\r\n" "{\r\n" +" long tot;\r\n" "\r\n" " /* Check for underflow */\r\n" " if ( current_record < 0 )\r\n" @@ -241,6 +274,10 @@ " exit( -1 );\r\n" " }\r\n" "\r\n" +" /* add the BASOP complexity to the counter */\r\n" +" tot = DeltaWeightedOperation();\r\n" +" ops_cnt += tot;\r\n" +"\r\n" " /* update count of current record */\r\n" " wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt;\r\n" " wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt;\r\n" @@ -250,6 +287,9 @@ " {\r\n" " current_record = wmops_caller_stack[--wmops_caller_stack_index];\r\n" " wmops[current_record].start_selfcnt = ops_cnt;\r\n" +"\r\n" +" /* set the ID of the previous BASOP counter */\r\n" +" Set_BASOP_WMOPS_counter( current_record );\r\n" " }\r\n" " else\r\n" " {\r\n" @@ -345,6 +385,10 @@ "#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" " wmops[i].current_call_number = 0;\r\n" "#endif\r\n" +"\r\n" +" /* update the WC of all BASOP counters */\r\n" +" Set_BASOP_WMOPS_counter( i );\r\n" +" Reset_BASOP_WMOPS_counter();\r\n" " }\r\n" "\r\n" " current_cnt = ops_cnt - start_cnt;\r\n" @@ -534,6 +578,24 @@ " }\r\n" "#endif\r\n" "\r\n" +" /* De-allocate the list of wmops record */\r\n" +" if ( wmops != NULL )\r\n" +" {\r\n" +" free( wmops );\r\n" +" }\r\n" +"\r\n" +" /* De-allocate the list of wmops caller functions */\r\n" +" if ( wmops_caller_stack != NULL )\r\n" +" {\r\n" +" free( wmops_caller_stack );\r\n" +" }\r\n" +"\r\n" +" /* De-allocate the BASOP WMOPS counter */\r\n" +" if ( multiCounter != NULL )\r\n" +" {\r\n" +" free( multiCounter );\r\n" +" }\r\n" +"\r\n" " return;\r\n" "}\r\n" "\r\n" @@ -1937,6 +1999,17 @@ " free( allocation_list );\r\n" " }\r\n" "\r\n" +" /* De-allocate list of stack records */\r\n" +" if ( stack_callers[0] != NULL )\r\n" +" {\r\n" +" free( stack_callers[0] );\r\n" +" }\r\n" +"\r\n" +" if ( stack_callers[1] != NULL )\r\n" +" {\r\n" +" free( stack_callers[1] );\r\n" +" }\r\n" +"\r\n" " /* De-allocate heap allocation call tree */\r\n" " if ( heap_allocation_call_tree != NULL )\r\n" " {\r\n" @@ -1975,4 +2048,100 @@ "int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */\r\n" "#endif\r\n" "\r\n" +"#ifdef WMOPS\r\n" +"/* Global counter for the calculation of BASOP complexity */\r\n" +"BASIC_OP *multiCounter = NULL;\r\n" +"int currCounter = 0;\r\n" +"int funcId_where_last_call_to_else_occurred;\r\n" +"long funcid_total_wmops_at_last_call_to_else;\r\n" +"int call_occurred = 1;\r\n" +"\r\n" +"const BASIC_OP op_weight = {\r\n" +" 1, 1, 1, 1, 1,\r\n" +" 1, 1, 1, 1, 1,\r\n" +" 1, 1, 1, 1, 1,\r\n" +" 1, 1, 2, 2, 1,\r\n" +" 1, 1, 1, 3, 1,\r\n" +"\r\n" +" 1, 1, 1, 3, 1,\r\n" +" 4, 1, 18, 1, 1,\r\n" +" 2, 1, 2, 2, 1,\r\n" +" 1, 1, 1, 1, 1,\r\n" +" 3, 3, 3, 3, 1,\r\n" +"\r\n" +" 1, 1, 1, 1, 1,\r\n" +" 1, 1, 1, 2,\r\n" +" 1, 2, 2, 4, 1,\r\n" +" 1, 1, 1, 1, 1,\r\n" +" 1, 1, 1, 1, 1,\r\n" +"\r\n" +" 1, 1, 1, 1, 3,\r\n" +" 3, 3, 3, 3, 1,\r\n" +" 1, 1, 1, 1, 1,\r\n" +" 1, 1, 1, 4, 4,\r\n" +" 4, 8, 3, 4, 4,\r\n" +"\r\n" +" 5, 32, 3\r\n" +"};\r\n" +"\r\n" +"/* Set the counter group to use, default is zero */\r\n" +"void Set_BASOP_WMOPS_counter( int counterId )\r\n" +"{\r\n" +" if ( ( counterId > num_wmops_records ) || ( counterId < 0 ) )\r\n" +" {\r\n" +" currCounter = 0;\r\n" +" return;\r\n" +" }\r\n" +" currCounter = counterId;\r\n" +" call_occurred = 1;\r\n" +"}\r\n" +"\r\n" +"long TotalWeightedOperation()\r\n" +"{\r\n" +" int i;\r\n" +" const long *ptr, *ptr2;\r\n" +" long tot; \r\n" +"\r\n" +" tot = 0;\r\n" +" ptr = (const long *) &multiCounter[currCounter];\r\n" +" ptr2 = (const long *) &op_weight;\r\n" +" for ( i = 0; i < ( int )( sizeof( multiCounter[currCounter] ) / sizeof( long ) ); i++ )\r\n" +" {\r\n" +" tot += ( ( *ptr++ ) * ( *ptr2++ ) );\r\n" +" }\r\n" +"\r\n" +" return ( (long) tot );\r\n" +"}\r\n" +"\r\n" +"long DeltaWeightedOperation()\r\n" +"{\r\n" +" long NewWOper, delta;\r\n" +"\r\n" +" NewWOper = TotalWeightedOperation();\r\n" +" delta = NewWOper - wmops[currCounter].LastWOper;\r\n" +" wmops[currCounter].LastWOper = NewWOper;\r\n" +"\r\n" +" return ( delta );\r\n" +"}\r\n" +"\r\n" +"/* Resets the current BASOP WMOPS counter */\r\n" +"void Reset_BASOP_WMOPS_counter( void )\r\n" +"{\r\n" +" int i;\r\n" +" long *ptr;\r\n" +"\r\n" +" /* clear the current BASOP operation counter before new frame begins */\r\n" +" ptr = (long *) &multiCounter[currCounter];\r\n" +" for ( i = 0; i < (int) ( sizeof( multiCounter[currCounter] ) / sizeof( long ) ); i++ )\r\n" +" {\r\n" +" *ptr++ = 0;\r\n" +" }\r\n" +"\r\n" +" wmops[currCounter].LastWOper = 0;\r\n" +"\r\n" +" return;\r\n" +"}\r\n" +"\r\n" +"#endif\r\n" +"\r\n" "\r\n" \ No newline at end of file diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index 938ba8f4..ccbaca49 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -29,12 +29,15 @@ "#pragma GCC system_header\r\n" "#endif\r\n" "\r\n" +"#ifndef INT_MAX\r\n" +"#define INT_MAX 32767\r\n" +"#endif\r\n" +"\r\n" "/* Real-time relationships */\r\n" "#define FRAMES_PER_SECOND 50.0 \r\n" "#define WMOPS_BOOST_FAC ( 1.0f ) /* scaling factor for equalizing the difference between automatic and manual instrumentation */\r\n" "#define FAC ( FRAMES_PER_SECOND / 1e6 * WMOPS_BOOST_FAC )\r\n" "\r\n" -"\r\n" "#ifdef WMOPS\r\n" "enum instructions\r\n" "{\r\n" @@ -560,7 +563,6 @@ "extern double ops_cnt;\r\n" "extern double prom_cnt;\r\n" "extern double inst_cnt[NUM_INST];\r\n" -"extern int ops_cnt_activ;\r\n" "\r\n" "void reset_wmops( void );\r\n" "void push_wmops( const char *label );\r\n" @@ -1035,5 +1037,405 @@ "\r\n" "#endif\r\n" "\r\n" +"\r\n" +"/* Global counter variable for calculation of complexity weight */\r\n" +"typedef struct\r\n" +"{\r\n" +" unsigned int add; /* Complexity Weight of 1 */\r\n" +" unsigned int sub; /* Complexity Weight of 1 */\r\n" +" unsigned int abs_s; /* Complexity Weight of 1 */\r\n" +" unsigned int shl; /* Complexity Weight of 1 */\r\n" +" unsigned int shr; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int extract_h; /* Complexity Weight of 1 */\r\n" +" unsigned int extract_l; /* Complexity Weight of 1 */\r\n" +" unsigned int mult; /* Complexity Weight of 1 */\r\n" +" unsigned int L_mult; /* Complexity Weight of 1 */\r\n" +" unsigned int negate; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int round; /* Complexity Weight of 1 */\r\n" +" unsigned int L_mac; /* Complexity Weight of 1 */\r\n" +" unsigned int L_msu; /* Complexity Weight of 1 */\r\n" +" unsigned int L_macNs; /* Complexity Weight of 1 */\r\n" +" unsigned int L_msuNs; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L_add; /* Complexity Weight of 1 */\r\n" +" unsigned int L_sub; /* Complexity Weight of 1 */\r\n" +" unsigned int L_add_c; /* Complexity Weight of 2 */\r\n" +" unsigned int L_sub_c; /* Complexity Weight of 2 */\r\n" +" unsigned int L_negate; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L_shl; /* Complexity Weight of 1 */\r\n" +" unsigned int L_shr; /* Complexity Weight of 1 */\r\n" +" unsigned int mult_r; /* Complexity Weight of 1 */\r\n" +" unsigned int shr_r; /* Complexity Weight of 3 */\r\n" +" unsigned int mac_r; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int msu_r; /* Complexity Weight of 1 */\r\n" +" unsigned int L_deposit_h; /* Complexity Weight of 1 */\r\n" +" unsigned int L_deposit_l; /* Complexity Weight of 1 */\r\n" +" unsigned int L_shr_r; /* Complexity Weight of 3 */\r\n" +" unsigned int L_abs; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L_sat; /* Complexity Weight of 4 */\r\n" +" unsigned int norm_s; /* Complexity Weight of 1 */\r\n" +" unsigned int div_s; /* Complexity Weight of 18 */\r\n" +" unsigned int norm_l; /* Complexity Weight of 1 */\r\n" +" unsigned int move16; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int move32; /* Complexity Weight of 2 */\r\n" +" unsigned int Logic16; /* Complexity Weight of 1 */\r\n" +" unsigned int Logic32; /* Complexity Weight of 2 */\r\n" +" unsigned int Test; /* Complexity Weight of 2 */\r\n" +" unsigned int s_max; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int s_min; /* Complexity Weight of 1 */\r\n" +" unsigned int L_max; /* Complexity Weight of 1 */\r\n" +" unsigned int L_min; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_max; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_min; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int shl_r; /* Complexity Weight of 3 */\r\n" +" unsigned int L_shl_r; /* Complexity Weight of 3 */\r\n" +" unsigned int L40_shr_r; /* Complexity Weight of 3 */\r\n" +" unsigned int L40_shl_r; /* Complexity Weight of 3 */\r\n" +" unsigned int norm_L40; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L40_shl; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_shr; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_negate; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_add; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_sub; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L40_abs; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_mult; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_mac; /* Complexity Weight of 1 */\r\n" +" unsigned int mac_r40; /* Complexity Weight of 2 */\r\n" +"\r\n" +" unsigned int L40_msu; /* Complexity Weight of 1 */\r\n" +" unsigned int msu_r40; /* Complexity Weight of 2 */\r\n" +" unsigned int Mpy_32_16_ss; /* Complexity Weight of 2 */\r\n" +" unsigned int Mpy_32_32_ss; /* Complexity Weight of 4 */\r\n" +" unsigned int L_mult0; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L_mac0; /* Complexity Weight of 1 */\r\n" +" unsigned int L_msu0; /* Complexity Weight of 1 */\r\n" +" unsigned int lshl; /* Complexity Weight of 1 */\r\n" +" unsigned int lshr; /* Complexity Weight of 1 */\r\n" +" unsigned int L_lshl; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L_lshr; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_lshl; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_lshr; /* Complexity Weight of 1 */\r\n" +" unsigned int s_and; /* Complexity Weight of 1 */\r\n" +" unsigned int s_or; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int s_xor; /* Complexity Weight of 1 */\r\n" +" unsigned int L_and; /* Complexity Weight of 1 */\r\n" +" unsigned int L_or; /* Complexity Weight of 1 */\r\n" +" unsigned int L_xor; /* Complexity Weight of 1 */\r\n" +" unsigned int rotl; /* Complexity Weight of 3 */\r\n" +"\r\n" +" unsigned int rotr; /* Complexity Weight of 3 */\r\n" +" unsigned int L_rotl; /* Complexity Weight of 3 */\r\n" +" unsigned int L_rotr; /* Complexity Weight of 3 */\r\n" +" unsigned int L40_set; /* Complexity Weight of 3 */\r\n" +" unsigned int L40_deposit_h; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L40_deposit_l; /* Complexity Weight of 1 */\r\n" +" unsigned int L40_deposit32; /* Complexity Weight of 1 */\r\n" +" unsigned int Extract40_H; /* Complexity Weight of 1 */\r\n" +" unsigned int Extract40_L; /* Complexity Weight of 1 */\r\n" +" unsigned int L_Extract40; /* Complexity Weight of 1 */\r\n" +"\r\n" +" unsigned int L40_round; /* Complexity Weight of 1 */\r\n" +" unsigned int L_saturate40; /* Complexity Weight of 1 */\r\n" +" unsigned int round40; /* Complexity Weight of 1 */\r\n" +" unsigned int If; /* Complexity Weight of 4 */\r\n" +" unsigned int Goto; /* Complexity Weight of 4 */\r\n" +"\r\n" +" unsigned int Break; /* Complexity Weight of 4 */\r\n" +" unsigned int Switch; /* Complexity Weight of 8 */\r\n" +" unsigned int For; /* Complexity Weight of 3 */\r\n" +" unsigned int While; /* Complexity Weight of 4 */\r\n" +" unsigned int Continue; /* Complexity Weight of 4 */\r\n" +"\r\n" +" unsigned int L_mls; /* Complexity Weight of 6 */\r\n" +" unsigned int div_l; /* Complexity Weight of 32 */\r\n" +" unsigned int i_mult; /* Complexity Weight of 3 */\r\n" +"} BASIC_OP;\r\n" +"\r\n" +"#ifdef WMOPS\r\n" +"extern BASIC_OP *multiCounter;\r\n" +"extern int currCounter;\r\n" +"\r\n" +"/* Technical note :\r\n" +" * The following 3 variables are only used for correct complexity\r\n" +" * evaluation of the following structure :\r\n" +" * IF{\r\n" +" * ...\r\n" +" * } ELSE IF {\r\n" +" * ...\r\n" +" * } ELSE IF {\r\n" +" * ...\r\n" +" * }\r\n" +" * ...\r\n" +" * } ELSE {\r\n" +" * ...\r\n" +" * }\r\n" +" */\r\n" +"extern int funcId_where_last_call_to_else_occurred;\r\n" +"extern long funcid_total_wmops_at_last_call_to_else;\r\n" +"extern int call_occurred;\r\n" +"\r\n" +"extern long TotalWeightedOperation( void );\r\n" +"long DeltaWeightedOperation();\r\n" +"\r\n" +"void Set_BASOP_WMOPS_counter( int counterId );\r\n" +"void Reset_BASOP_WMOPS_counter( void );\r\n" +"\r\n" +"#endif\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : FOR\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro FOR should be used instead of the 'for' C statement.\r\n" +" * The complexity is independent of the number of loop iterations that are\r\n" +" * performed.\r\n" +" *\r\n" +" * Complexity weight : 3 (regardless of number of iterations).\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define FOR( a) for( a)\r\n" +"\r\n" +"#else \r\n" +"#define FOR( a) if( incrFor(), 0); else for( a)\r\n" +"\r\n" +"static __inline void incrFor( void) {\r\n" +" multiCounter[currCounter].For++;\r\n" +"}\r\n" +"#endif \r\n" +"\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : WHILE\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro WHILE should be used instead of the 'while' C statement.\r\n" +" * The complexity is proportional to the number of loop iterations that\r\n" +" * are performed.\r\n" +" *\r\n" +" * Complexity weight : 4 x 'number of loop iterations'.\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define WHILE( a) while( a)\r\n" +"\r\n" +"#else \r\n" +"#define WHILE( a) while( incrWhile(), a)\r\n" +"\r\n" +"static __inline void incrWhile( void) {\r\n" +" multiCounter[currCounter].While++;\r\n" +"}\r\n" +"#endif \r\n" +"\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : DO\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro DO should be used instead of the 'do' C statement.\r\n" +" *\r\n" +" * Complexity weight : 0 (complexity counted by WHILE macro).\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define DO do\r\n" +"\r\n" +"#else \r\n" +"#define DO do\r\n" +"\r\n" +"#endif \r\n" +"\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : IF\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro IF should :\r\n" +" *\r\n" +" * - not be used when :\r\n" +" * - the 'if' structure does not have any 'else if' nor 'else' statement\r\n" +" * - and it conditions only one DSP basic operations.\r\n" +" *\r\n" +" * - be used instead of the 'if' C statement in every other case :\r\n" +" * - when there is an 'else' or 'else if' statement,\r\n" +" * - or when the 'if' conditions several DSP basic operations,\r\n" +" * - or when the 'if' conditions a function call.\r\n" +" *\r\n" +" * Complexity weight : 4\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define IF( a) if( a)\r\n" +"\r\n" +"#else \r\n" +"#define IF( a) if( incrIf(), a)\r\n" +"\r\n" +"static __inline void incrIf( void) {\r\n" +" /* Technical note :\r\n" +" * If the \"IF\" operator comes just after an \"ELSE\", its counter\r\n" +" * must not be incremented.\r\n" +" */\r\n" +" if ( ( currCounter != funcId_where_last_call_to_else_occurred ) || ( TotalWeightedOperation() != funcid_total_wmops_at_last_call_to_else ) || ( call_occurred == 1 ) )\r\n" +" {\r\n" +" multiCounter[currCounter].If++;\r\n" +" }\r\n" +"\r\n" +" call_occurred = 0;\r\n" +" funcId_where_last_call_to_else_occurred = INT_MAX;\r\n" +"}\r\n" +"#endif \r\n" +"\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : ELSE\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro ELSE should be used instead of the 'else' C statement.\r\n" +" *\r\n" +" * Complexity weight : 4\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define ELSE else\r\n" +"\r\n" +"#else \r\n" +"#define ELSE else if( incrElse(), 0) ; else\r\n" +"\r\n" +"static __inline void incrElse( void) {\r\n" +" multiCounter[currCounter].If++;\r\n" +"\r\n" +" /* We keep track of the funcId of the last function\r\n" +" * which used ELSE {...} structure.\r\n" +" */\r\n" +" funcId_where_last_call_to_else_occurred = currCounter;\r\n" +"\r\n" +" /* We keep track of the number of WMOPS of this funcId\r\n" +" * when the ELSE macro was called.\r\n" +" */\r\n" +" funcid_total_wmops_at_last_call_to_else = TotalWeightedOperation();\r\n" +"\r\n" +" /* call_occurred is set to 0, in order to count the next IF (if necessary)\r\n" +" */\r\n" +" call_occurred = 0;\r\n" +"}\r\n" +"#endif \r\n" +"\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : SWITCH\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro SWITCH should be used instead of the 'switch' C statement.\r\n" +" *\r\n" +" * Complexity weight : 8\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define SWITCH( a) switch( a)\r\n" +"\r\n" +"#else \r\n" +"#define SWITCH( a) switch( incrSwitch(), a)\r\n" +"\r\n" +"static __inline void incrSwitch( void) {\r\n" +" multiCounter[currCounter].Switch++;\r\n" +"}\r\n" +"#endif \r\n" +"\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : CONTINUE\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro CONTINUE should be used instead of the 'continue' C statement.\r\n" +" *\r\n" +" * Complexity weight : 4\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define CONTINUE continue\r\n" +"\r\n" +"#else \r\n" +"#define CONTINUE if( incrContinue(), 0); else continue\r\n" +"\r\n" +"static __inline void incrContinue( void) {\r\n" +" multiCounter[currCounter].Continue++;\r\n" +"}\r\n" +"#endif \r\n" +"\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : BREAK\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro BREAK should be used instead of the 'break' C statement.\r\n" +" *\r\n" +" * Complexity weight : 4\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define BREAK break\r\n" +"\r\n" +"#else \r\n" +"#define BREAK if( incrBreak(), 0) break; else break\r\n" +"\r\n" +"static __inline void incrBreak( void) {\r\n" +" multiCounter[currCounter].Break++;\r\n" +"}\r\n" +"#endif \r\n" +"\r\n" +"\r\n" +"/*****************************************************************************\r\n" +" *\r\n" +" * Function Name : GOTO\r\n" +" *\r\n" +" * Purpose :\r\n" +" *\r\n" +" * The macro GOTO should be used instead of the 'goto' C statement.\r\n" +" *\r\n" +" * Complexity weight : 4\r\n" +" *\r\n" +" *****************************************************************************/\r\n" +"#ifndef WMOPS\r\n" +"#define GOTO goto\r\n" +"\r\n" +"#else \r\n" +"#define GOTO if( incrGoto(), 0); else goto\r\n" +"\r\n" +"static __inline void incrGoto( void) {\r\n" +" multiCounter[currCounter].Goto++;\r\n" +"}\r\n" +"#endif \r\n" +"\r\n" "#endif /* WMOPS_H */\r\n" +"\r\n" "\r\n" \ No newline at end of file From 5bab9d22b2f59f0539030c0fd0d29f84e19706cf Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 27 Sep 2023 09:34:13 +0200 Subject: [PATCH 23/53] new reference files wmc_auto.[c|h] for ctest --- src/wmc_tool/test_data/ref/wmc_auto.c | 171 ++++++++++- src/wmc_tool/test_data/ref/wmc_auto.h | 406 +++++++++++++++++++++++++- 2 files changed, 574 insertions(+), 3 deletions(-) diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index 8e70ebe2..00c28964 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -39,7 +39,6 @@ #define MAX_PARAMS_LENGTH 50 /* Maximum length of the function parameter string */ #define MAX_NUM_RECORDS 300 /* Initial maximum number of records -> mightb be increased during runtime, if needed */ #define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of records, increase the number of records by this number */ - #define MAX_CALL_TREE_DEPTH 100 /* maximum depth of the function call tree */ #define DOUBLE_MAX 0x80000000 @@ -49,6 +48,7 @@ typedef struct long call_number; long update_cnt; int call_tree[MAX_CALL_TREE_DEPTH]; + long LastWOper; double start_selfcnt; double current_selfcnt; double max_selfcnt; @@ -86,6 +86,7 @@ static int *heap_allocation_call_tree = NULL, heap_allocation_call_tree_size = 0 void reset_wmops( void ) { int i, j; + unsigned int *ptr; num_wmops_records = 0; max_num_wmops_records = MAX_NUM_RECORDS; @@ -109,6 +110,20 @@ void reset_wmops( void ) exit( -1 ); } + /* allocate the BASOP WMOPS counter */ + if ( multiCounter == NULL ) + { + multiCounter = (BASIC_OP *) malloc( max_num_wmops_records * sizeof( BASIC_OP ) ); + } + + if ( multiCounter == NULL ) + { + fprintf( stderr, "Error: Unable to Allocate the BASOP WMOPS counter!" ); + exit( -1 ); + } + + /* initilize the list of wmops records */ + /* initilize the BASOP WMOPS counters */ for ( i = 0; i < max_num_wmops_records; i++ ) { strcpy( &wmops[i].label[0], "\0" ); @@ -134,6 +149,14 @@ void reset_wmops( void ) wmops[i].current_call_number = 0; wmops[i].wc_call_number = -1; #endif + + /* clear all BASOP operation counters */ + ptr = (unsigned int*) &multiCounter[i]; + for ( j = 0; j < (int) ( sizeof(BASIC_OP ) / sizeof( unsigned int ) ); j++ ) + { + *ptr++ = 0; + } + wmops[i].LastWOper = 0; } /* allocate the list of wmops callers to track the sequence of function calls */ @@ -155,6 +178,10 @@ void reset_wmops( void ) wmops_caller_stack[i] = -1; } + /* initialize auxiliary BASOP WMOPS variables */ + call_occurred = 1; + funcId_where_last_call_to_else_occurred = INT_MAX; + return; } @@ -183,9 +210,11 @@ void push_wmops( const char *label ) /* There is no room for a new wmops record -> reallocate the list */ max_num_wmops_records += MAX_NUM_RECORDS_REALLOC_STEP; wmops = realloc( wmops, max_num_wmops_records * sizeof( wmops_record ) ); + multiCounter = realloc( multiCounter, max_num_wmops_records * sizeof( BASIC_OP ) ); } strcpy( wmops[i].label, label ); + num_wmops_records++; } @@ -227,12 +256,16 @@ void push_wmops( const char *label ) wmops[current_record].current_call_number++; #endif + /* set the ID of BASOP functions counters */ + Set_BASOP_WMOPS_counter( current_record ); + return; } void pop_wmops( void ) { + long tot; /* Check for underflow */ if ( current_record < 0 ) @@ -241,6 +274,10 @@ void pop_wmops( void ) exit( -1 ); } + /* add the BASOP complexity to the counter */ + tot = DeltaWeightedOperation(); + ops_cnt += tot; + /* update count of current record */ wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt; @@ -250,6 +287,9 @@ void pop_wmops( void ) { current_record = wmops_caller_stack[--wmops_caller_stack_index]; wmops[current_record].start_selfcnt = ops_cnt; + + /* set the ID of the previous BASOP counter */ + Set_BASOP_WMOPS_counter( current_record ); } else { @@ -345,6 +385,10 @@ void update_wmops( void ) #ifdef WMOPS_WC_FRAME_ANALYSIS wmops[i].current_call_number = 0; #endif + + /* update the WC of all BASOP counters */ + Set_BASOP_WMOPS_counter( i ); + Reset_BASOP_WMOPS_counter(); } current_cnt = ops_cnt - start_cnt; @@ -534,6 +578,24 @@ void print_wmops( void ) } #endif + /* De-allocate the list of wmops record */ + if ( wmops != NULL ) + { + free( wmops ); + } + + /* De-allocate the list of wmops caller functions */ + if ( wmops_caller_stack != NULL ) + { + free( wmops_caller_stack ); + } + + /* De-allocate the BASOP WMOPS counter */ + if ( multiCounter != NULL ) + { + free( multiCounter ); + } + return; } @@ -1937,6 +1999,17 @@ void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) free( allocation_list ); } + /* De-allocate list of stack records */ + if ( stack_callers[0] != NULL ) + { + free( stack_callers[0] ); + } + + if ( stack_callers[1] != NULL ) + { + free( stack_callers[1] ); + } + /* De-allocate heap allocation call tree */ if ( heap_allocation_call_tree != NULL ) { @@ -1975,4 +2048,100 @@ void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */ #endif +#ifdef WMOPS +/* Global counter for the calculation of BASOP complexity */ +BASIC_OP *multiCounter = NULL; +int currCounter = 0; +int funcId_where_last_call_to_else_occurred; +long funcid_total_wmops_at_last_call_to_else; +int call_occurred = 1; + +const BASIC_OP op_weight = { + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 2, 2, 1, + 1, 1, 1, 3, 1, + + 1, 1, 1, 3, 1, + 4, 1, 18, 1, 1, + 2, 1, 2, 2, 1, + 1, 1, 1, 1, 1, + 3, 3, 3, 3, 1, + + 1, 1, 1, 1, 1, + 1, 1, 1, 2, + 1, 2, 2, 4, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 3, + 3, 3, 3, 3, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 4, 4, + 4, 8, 3, 4, 4, + + 5, 32, 3 +}; + +/* Set the counter group to use, default is zero */ +void Set_BASOP_WMOPS_counter( int counterId ) +{ + if ( ( counterId > num_wmops_records ) || ( counterId < 0 ) ) + { + currCounter = 0; + return; + } + currCounter = counterId; + call_occurred = 1; +} + +long TotalWeightedOperation() +{ + int i; + const long *ptr, *ptr2; + long tot; + + tot = 0; + ptr = (const long *) &multiCounter[currCounter]; + ptr2 = (const long *) &op_weight; + for ( i = 0; i < ( int )( sizeof( multiCounter[currCounter] ) / sizeof( long ) ); i++ ) + { + tot += ( ( *ptr++ ) * ( *ptr2++ ) ); + } + + return ( (long) tot ); +} + +long DeltaWeightedOperation() +{ + long NewWOper, delta; + + NewWOper = TotalWeightedOperation(); + delta = NewWOper - wmops[currCounter].LastWOper; + wmops[currCounter].LastWOper = NewWOper; + + return ( delta ); +} + +/* Resets the current BASOP WMOPS counter */ +void Reset_BASOP_WMOPS_counter( void ) +{ + int i; + long *ptr; + + /* clear the current BASOP operation counter before new frame begins */ + ptr = (long *) &multiCounter[currCounter]; + for ( i = 0; i < (int) ( sizeof( multiCounter[currCounter] ) / sizeof( long ) ); i++ ) + { + *ptr++ = 0; + } + + wmops[currCounter].LastWOper = 0; + + return; +} + +#endif + diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index 767a02dc..be056e9c 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.h +++ b/src/wmc_tool/test_data/ref/wmc_auto.h @@ -29,12 +29,15 @@ #pragma GCC system_header #endif +#ifndef INT_MAX +#define INT_MAX 32767 +#endif + /* Real-time relationships */ #define FRAMES_PER_SECOND 50.0 #define WMOPS_BOOST_FAC ( 1.0f ) /* scaling factor for equalizing the difference between automatic and manual instrumentation */ #define FAC ( FRAMES_PER_SECOND / 1e6 * WMOPS_BOOST_FAC ) - #ifdef WMOPS enum instructions { @@ -560,7 +563,6 @@ enum instructions extern double ops_cnt; extern double prom_cnt; extern double inst_cnt[NUM_INST]; -extern int ops_cnt_activ; void reset_wmops( void ); void push_wmops( const char *label ); @@ -1035,5 +1037,405 @@ void reset_stack( void ); #endif + +/* Global counter variable for calculation of complexity weight */ +typedef struct +{ + unsigned int add; /* Complexity Weight of 1 */ + unsigned int sub; /* Complexity Weight of 1 */ + unsigned int abs_s; /* Complexity Weight of 1 */ + unsigned int shl; /* Complexity Weight of 1 */ + unsigned int shr; /* Complexity Weight of 1 */ + + unsigned int extract_h; /* Complexity Weight of 1 */ + unsigned int extract_l; /* Complexity Weight of 1 */ + unsigned int mult; /* Complexity Weight of 1 */ + unsigned int L_mult; /* Complexity Weight of 1 */ + unsigned int negate; /* Complexity Weight of 1 */ + + unsigned int round; /* Complexity Weight of 1 */ + unsigned int L_mac; /* Complexity Weight of 1 */ + unsigned int L_msu; /* Complexity Weight of 1 */ + unsigned int L_macNs; /* Complexity Weight of 1 */ + unsigned int L_msuNs; /* Complexity Weight of 1 */ + + unsigned int L_add; /* Complexity Weight of 1 */ + unsigned int L_sub; /* Complexity Weight of 1 */ + unsigned int L_add_c; /* Complexity Weight of 2 */ + unsigned int L_sub_c; /* Complexity Weight of 2 */ + unsigned int L_negate; /* Complexity Weight of 1 */ + + unsigned int L_shl; /* Complexity Weight of 1 */ + unsigned int L_shr; /* Complexity Weight of 1 */ + unsigned int mult_r; /* Complexity Weight of 1 */ + unsigned int shr_r; /* Complexity Weight of 3 */ + unsigned int mac_r; /* Complexity Weight of 1 */ + + unsigned int msu_r; /* Complexity Weight of 1 */ + unsigned int L_deposit_h; /* Complexity Weight of 1 */ + unsigned int L_deposit_l; /* Complexity Weight of 1 */ + unsigned int L_shr_r; /* Complexity Weight of 3 */ + unsigned int L_abs; /* Complexity Weight of 1 */ + + unsigned int L_sat; /* Complexity Weight of 4 */ + unsigned int norm_s; /* Complexity Weight of 1 */ + unsigned int div_s; /* Complexity Weight of 18 */ + unsigned int norm_l; /* Complexity Weight of 1 */ + unsigned int move16; /* Complexity Weight of 1 */ + + unsigned int move32; /* Complexity Weight of 2 */ + unsigned int Logic16; /* Complexity Weight of 1 */ + unsigned int Logic32; /* Complexity Weight of 2 */ + unsigned int Test; /* Complexity Weight of 2 */ + unsigned int s_max; /* Complexity Weight of 1 */ + + unsigned int s_min; /* Complexity Weight of 1 */ + unsigned int L_max; /* Complexity Weight of 1 */ + unsigned int L_min; /* Complexity Weight of 1 */ + unsigned int L40_max; /* Complexity Weight of 1 */ + unsigned int L40_min; /* Complexity Weight of 1 */ + + unsigned int shl_r; /* Complexity Weight of 3 */ + unsigned int L_shl_r; /* Complexity Weight of 3 */ + unsigned int L40_shr_r; /* Complexity Weight of 3 */ + unsigned int L40_shl_r; /* Complexity Weight of 3 */ + unsigned int norm_L40; /* Complexity Weight of 1 */ + + unsigned int L40_shl; /* Complexity Weight of 1 */ + unsigned int L40_shr; /* Complexity Weight of 1 */ + unsigned int L40_negate; /* Complexity Weight of 1 */ + unsigned int L40_add; /* Complexity Weight of 1 */ + unsigned int L40_sub; /* Complexity Weight of 1 */ + + unsigned int L40_abs; /* Complexity Weight of 1 */ + unsigned int L40_mult; /* Complexity Weight of 1 */ + unsigned int L40_mac; /* Complexity Weight of 1 */ + unsigned int mac_r40; /* Complexity Weight of 2 */ + + unsigned int L40_msu; /* Complexity Weight of 1 */ + unsigned int msu_r40; /* Complexity Weight of 2 */ + unsigned int Mpy_32_16_ss; /* Complexity Weight of 2 */ + unsigned int Mpy_32_32_ss; /* Complexity Weight of 4 */ + unsigned int L_mult0; /* Complexity Weight of 1 */ + + unsigned int L_mac0; /* Complexity Weight of 1 */ + unsigned int L_msu0; /* Complexity Weight of 1 */ + unsigned int lshl; /* Complexity Weight of 1 */ + unsigned int lshr; /* Complexity Weight of 1 */ + unsigned int L_lshl; /* Complexity Weight of 1 */ + + unsigned int L_lshr; /* Complexity Weight of 1 */ + unsigned int L40_lshl; /* Complexity Weight of 1 */ + unsigned int L40_lshr; /* Complexity Weight of 1 */ + unsigned int s_and; /* Complexity Weight of 1 */ + unsigned int s_or; /* Complexity Weight of 1 */ + + unsigned int s_xor; /* Complexity Weight of 1 */ + unsigned int L_and; /* Complexity Weight of 1 */ + unsigned int L_or; /* Complexity Weight of 1 */ + unsigned int L_xor; /* Complexity Weight of 1 */ + unsigned int rotl; /* Complexity Weight of 3 */ + + unsigned int rotr; /* Complexity Weight of 3 */ + unsigned int L_rotl; /* Complexity Weight of 3 */ + unsigned int L_rotr; /* Complexity Weight of 3 */ + unsigned int L40_set; /* Complexity Weight of 3 */ + unsigned int L40_deposit_h; /* Complexity Weight of 1 */ + + unsigned int L40_deposit_l; /* Complexity Weight of 1 */ + unsigned int L40_deposit32; /* Complexity Weight of 1 */ + unsigned int Extract40_H; /* Complexity Weight of 1 */ + unsigned int Extract40_L; /* Complexity Weight of 1 */ + unsigned int L_Extract40; /* Complexity Weight of 1 */ + + unsigned int L40_round; /* Complexity Weight of 1 */ + unsigned int L_saturate40; /* Complexity Weight of 1 */ + unsigned int round40; /* Complexity Weight of 1 */ + unsigned int If; /* Complexity Weight of 4 */ + unsigned int Goto; /* Complexity Weight of 4 */ + + unsigned int Break; /* Complexity Weight of 4 */ + unsigned int Switch; /* Complexity Weight of 8 */ + unsigned int For; /* Complexity Weight of 3 */ + unsigned int While; /* Complexity Weight of 4 */ + unsigned int Continue; /* Complexity Weight of 4 */ + + unsigned int L_mls; /* Complexity Weight of 6 */ + unsigned int div_l; /* Complexity Weight of 32 */ + unsigned int i_mult; /* Complexity Weight of 3 */ +} BASIC_OP; + +#ifdef WMOPS +extern BASIC_OP *multiCounter; +extern int currCounter; + +/* Technical note : + * The following 3 variables are only used for correct complexity + * evaluation of the following structure : + * IF{ + * ... + * } ELSE IF { + * ... + * } ELSE IF { + * ... + * } + * ... + * } ELSE { + * ... + * } + */ +extern int funcId_where_last_call_to_else_occurred; +extern long funcid_total_wmops_at_last_call_to_else; +extern int call_occurred; + +extern long TotalWeightedOperation( void ); +long DeltaWeightedOperation(); + +void Set_BASOP_WMOPS_counter( int counterId ); +void Reset_BASOP_WMOPS_counter( void ); + +#endif + +/***************************************************************************** + * + * Function Name : FOR + * + * Purpose : + * + * The macro FOR should be used instead of the 'for' C statement. + * The complexity is independent of the number of loop iterations that are + * performed. + * + * Complexity weight : 3 (regardless of number of iterations). + * + *****************************************************************************/ +#ifndef WMOPS +#define FOR( a) for( a) + +#else +#define FOR( a) if( incrFor(), 0); else for( a) + +static __inline void incrFor( void) { + multiCounter[currCounter].For++; +} +#endif + + +/***************************************************************************** + * + * Function Name : WHILE + * + * Purpose : + * + * The macro WHILE should be used instead of the 'while' C statement. + * The complexity is proportional to the number of loop iterations that + * are performed. + * + * Complexity weight : 4 x 'number of loop iterations'. + * + *****************************************************************************/ +#ifndef WMOPS +#define WHILE( a) while( a) + +#else +#define WHILE( a) while( incrWhile(), a) + +static __inline void incrWhile( void) { + multiCounter[currCounter].While++; +} +#endif + + +/***************************************************************************** + * + * Function Name : DO + * + * Purpose : + * + * The macro DO should be used instead of the 'do' C statement. + * + * Complexity weight : 0 (complexity counted by WHILE macro). + * + *****************************************************************************/ +#ifndef WMOPS +#define DO do + +#else +#define DO do + +#endif + + +/***************************************************************************** + * + * Function Name : IF + * + * Purpose : + * + * The macro IF should : + * + * - not be used when : + * - the 'if' structure does not have any 'else if' nor 'else' statement + * - and it conditions only one DSP basic operations. + * + * - be used instead of the 'if' C statement in every other case : + * - when there is an 'else' or 'else if' statement, + * - or when the 'if' conditions several DSP basic operations, + * - or when the 'if' conditions a function call. + * + * Complexity weight : 4 + * + *****************************************************************************/ +#ifndef WMOPS +#define IF( a) if( a) + +#else +#define IF( a) if( incrIf(), a) + +static __inline void incrIf( void) { + /* Technical note : + * If the "IF" operator comes just after an "ELSE", its counter + * must not be incremented. + */ + if ( ( currCounter != funcId_where_last_call_to_else_occurred ) || ( TotalWeightedOperation() != funcid_total_wmops_at_last_call_to_else ) || ( call_occurred == 1 ) ) + { + multiCounter[currCounter].If++; + } + + call_occurred = 0; + funcId_where_last_call_to_else_occurred = INT_MAX; +} +#endif + + +/***************************************************************************** + * + * Function Name : ELSE + * + * Purpose : + * + * The macro ELSE should be used instead of the 'else' C statement. + * + * Complexity weight : 4 + * + *****************************************************************************/ +#ifndef WMOPS +#define ELSE else + +#else +#define ELSE else if( incrElse(), 0) ; else + +static __inline void incrElse( void) { + multiCounter[currCounter].If++; + + /* We keep track of the funcId of the last function + * which used ELSE {...} structure. + */ + funcId_where_last_call_to_else_occurred = currCounter; + + /* We keep track of the number of WMOPS of this funcId + * when the ELSE macro was called. + */ + funcid_total_wmops_at_last_call_to_else = TotalWeightedOperation(); + + /* call_occurred is set to 0, in order to count the next IF (if necessary) + */ + call_occurred = 0; +} +#endif + + +/***************************************************************************** + * + * Function Name : SWITCH + * + * Purpose : + * + * The macro SWITCH should be used instead of the 'switch' C statement. + * + * Complexity weight : 8 + * + *****************************************************************************/ +#ifndef WMOPS +#define SWITCH( a) switch( a) + +#else +#define SWITCH( a) switch( incrSwitch(), a) + +static __inline void incrSwitch( void) { + multiCounter[currCounter].Switch++; +} +#endif + + +/***************************************************************************** + * + * Function Name : CONTINUE + * + * Purpose : + * + * The macro CONTINUE should be used instead of the 'continue' C statement. + * + * Complexity weight : 4 + * + *****************************************************************************/ +#ifndef WMOPS +#define CONTINUE continue + +#else +#define CONTINUE if( incrContinue(), 0); else continue + +static __inline void incrContinue( void) { + multiCounter[currCounter].Continue++; +} +#endif + + +/***************************************************************************** + * + * Function Name : BREAK + * + * Purpose : + * + * The macro BREAK should be used instead of the 'break' C statement. + * + * Complexity weight : 4 + * + *****************************************************************************/ +#ifndef WMOPS +#define BREAK break + +#else +#define BREAK if( incrBreak(), 0) break; else break + +static __inline void incrBreak( void) { + multiCounter[currCounter].Break++; +} +#endif + + +/***************************************************************************** + * + * Function Name : GOTO + * + * Purpose : + * + * The macro GOTO should be used instead of the 'goto' C statement. + * + * Complexity weight : 4 + * + *****************************************************************************/ +#ifndef WMOPS +#define GOTO goto + +#else +#define GOTO if( incrGoto(), 0); else goto + +static __inline void incrGoto( void) { + multiCounter[currCounter].Goto++; +} +#endif + #endif /* WMOPS_H */ + From 2c55841d50841b225982e36dfc212fe7e8de95c8 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 27 Sep 2023 09:34:52 +0200 Subject: [PATCH 24/53] unifiy macros and functions under WMOPS_FUNCTS_STRING --- src/wmc_tool/c_parser.cpp | 42 ++++++++++----------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index a37863e3..c1975c7b 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -110,29 +110,13 @@ MANUAL_COUNTING_MACROS_STRING \ AUTO_COUNTING_MACROS_STRING -#define WMOPS_COUNTLIB_FUNCTS_STRING \ - "reset_wmops push_wmops pop_wmops update_wmops print_wmops " -#define OTHER_COUNTLIB_FUNCTS_STRING \ - "Dyn_Mem_Init Dyn_Mem_Exit Dyn_Mem_Exit_noprint " \ - "Dyn_Mem_In Dyn_Mem_Add Dyn_Mem_Out " \ - "Sta_Mem_Init Sta_Mem_Exit Sta_Mem_Exit_noprint Sta_Mem_Add " \ - "P_Dyn_Mem_Init P_Dyn_Mem_Exit P_Dyn_Mem_Exit_noprint " \ - "P_Dyn_Mem_In P_Dyn_Mem_Add P_Dyn_Mem_Out " \ - "P_Sta_Mem_Init P_Sta_Mem_Exit P_Sta_Mem_Exit_noprint P_Sta_Mem_Add " \ - "DYN_MEM_IN DYN_MEM_ADD DYN_MEM_OUT " -#define COUNTLIB_FUNCTS_STRING \ - WMOPS_COUNTLIB_FUNCTS_STRING \ - OTHER_COUNTLIB_FUNCTS_STRING +#define WMOPS_FUNCTS_STRING \ + "reset_wmops push_wmops pop_wmops update_wmops print_wmops print_mem " \ + "rsize Get_Const_Data_Size Print_Const_Data_Size " #define COUNTING_CODE_STRING \ COUNTING_MACROS_STRING \ - COUNTLIB_FUNCTS_STRING - -#define TOOL_FUNCTS_STRING \ - "rsize " \ - "Get_Const_Data_Size " \ - "Print_Const_Data_Size " \ - "print_mem " + WMOPS_FUNCTS_STRING #define SYSTEM_ALLOC_FUNCTS_STRING \ "malloc calloc free " @@ -4204,12 +4188,12 @@ static Item_Type Get_Call_Type( return ITEM_FUNC_COUNTERS_AUTO_R | ITEM_INSTRUMENTATION | ITEM_SKIPPED; } /* Any of the Counting Library Functions? */ - if ( memwordcmp( name_start, COUNTLIB_FUNCTS_STRING ) != NULL ) - { /* Yes */ - return ITEM_FUNC_COUNT_LIB | ITEM_SKIPPED; - } - /* Any of the ROM Counting Macros? */ - if ( memwordcmp( name_start, TOOL_FUNCTS_STRING ) != NULL ) + //if ( memwordcmp( name_start, COUNTLIB_FUNCTS_STRING ) != NULL ) + //{ /* Yes */ + // return ITEM_FUNC_COUNT_LIB | ITEM_SKIPPED; + //} + /* Any of the WMOPS Library Functions or Macros? */ + if ( memwordcmp( name_start, WMOPS_FUNCTS_STRING) != NULL ) { /* Yes */ return ITEM_FUNC_COUNT_LIB | ITEM_SKIPPED; } @@ -8714,11 +8698,7 @@ void Print_Information( void ) fprintf(stdout, "\n Manual Macros Removed during Desintrumentation:\n" ); Print_Words( MANUAL_COUNTING_MACROS_STRING ); fprintf(stdout, "\n WMOPS Library Functions that are not Instrumented:\n" ); - Print_Words( WMOPS_COUNTLIB_FUNCTS_STRING, true ); - fprintf(stdout, "\n Other Counting Functions that are not Instrumented:\n" ); - Print_Words( OTHER_COUNTLIB_FUNCTS_STRING ); - fprintf(stdout, "\n WMC_Tool Functions that are not Instrumented:\n" ); - Print_Words( TOOL_FUNCTS_STRING, true ); + Print_Words( WMOPS_FUNCTS_STRING, true ); fprintf(stdout, "\n System Functions that are not Instrumented:\n" ); Print_Words( SYSTEM_FUNCTS_STRING ); fprintf(stdout, "\n System Functions that are Instrumented:\n" ); From 64e18f2ab898894bed014069878ac32404073900 Mon Sep 17 00:00:00 2001 From: malenov Date: Mon, 2 Oct 2023 14:54:08 +0200 Subject: [PATCH 25/53] count PROM size of BASOP functions and operators --- src/wmc_tool/c_parser.cpp | 135 +++++++++++++++++++++++++----------- src/wmc_tool/constants.h | 3 +- src/wmc_tool/parsing_defs.h | 1 + 3 files changed, 96 insertions(+), 43 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index c1975c7b..bfbf24cc 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -148,6 +148,18 @@ "set_min set_max " \ "mac msu " +#define BASOP_FUNCTS_STRING \ + "add sub abs_s shl shr extract_h extract_l mult L_mult negate round " \ + "L_mac L_msu L_macNs L_msuNs L_add L_sub L_add_c L_sub_c L_negate L_shl L_shr " \ + "mult_r shr_r mac_r msu_r L_deposit_h L_deposit_l L_shr_r L_abs L_sat norm_s div_s " \ + "norm_l move16 move32 Logic16 Logic32 Test s_max s_min L_max L_min L40_max " \ + "L40_min shl_r L_shl_r L40_shr_r L40_shl_r norm_L40 L40_shl L40_shr L40_negate " \ + "L40_add L40_sub L40_abs L40_mult L40_mac mac_r40 L40_msu msu_r40 Mpy_32_16_ss " \ + "Mpy_32_32_ss L_mult0 L_mac0 L_msu0 lshl lshr L_lshl L_lshr L40_lshl L40_lshr " \ + "s_and s_or s_xor L_and L_or L_xor rotl rotr L_rotl L_rotr L40_set L40_deposit_h " \ + "L40_deposit_l L40_deposit32 Extract40_H Extract40_L L_Extract40 L40_round " \ + "L_saturate40 round40 If Goto Break Switch For While Continue L_mls div_l i_mult " + #define WMOPS_LIB_INCLUDE_STRING \ "wmc_auto.h" @@ -180,15 +192,6 @@ #define RETURN_KW_STRING "return" -/***************************************** - * Fixed Point Macros Lacking a ';' - *****************************************/ - -#define NON_STANDARD_FXP_MACROS_STRING \ - "BASOP_SATURATE_WARNING_ON " \ - "BASOP_SATURATE_WARNING_OFF " \ - "BASOP_SATURATE_ERROR_ON " \ - "BASOP_SATURATE_ERROR_OFF " /*-------------------------------------------------------------------* * Local Typedefs @@ -2501,6 +2504,9 @@ static TOOL_ERROR Find_Keywords( { /* Yes */ /* Try Uppercase Version */ for (ns = kw_name; *ns != NUL_CHAR; *ns = toupper(*ns), ns++); + + /* Uppercase version is recognized as BASOP operation -> do not instrument */ + item_type = ITEM_INSTRUMENTATION_OFF; } else if (run == 2) { @@ -2572,6 +2578,7 @@ static TOOL_ERROR Find_Keywords( { /* Set Name End */ ne = ns + kw_name_len; + /* Add Keyword Region */ if ( ( ErrCode = Add_Region( ParseTbl_ptr, item_type + /* Keywords that Compute to Constants are stripped of KW Attribute. */ @@ -2661,8 +2668,7 @@ static TOOL_ERROR Find_Keywords( dp = pe; } /* Skip Blanks to the Left (this cannot fail) */ - pe = Skip_Chars( pe - 1, BLANK_CHARS, ParseTbl_ptr, - ITEM_ANY, ITEM_MOVED_OVER, BACKWARDS ); + pe = Skip_Chars( pe - 1, BLANK_CHARS, ParseTbl_ptr, ITEM_ANY, ITEM_MOVED_OVER, BACKWARDS ); } /* Keyword Name has a Delimiter? */ if ( keywords[i].kw_delim != NUL_CHAR ) @@ -2728,15 +2734,17 @@ static TOOL_ERROR Find_Keywords( } /* Add Keyword Expression Region */ - if ( ( ErrCode = Add_Region( ParseTbl_ptr, + /* !!! VM: added ITEM_INSTRUMENTATION_OFF to mark a non-instrumented BASOP keyword expression region !!! */ + if ( ( ErrCode = Add_Region( ParseTbl_ptr, (item_type == ITEM_INSTRUMENTATION_OFF ? ITEM_INSTRUMENTATION_OFF : 0) + /* Keywords Expressions that Compute to Constants are stripped of KW_EXPR Attribute */ /* Added because when 'sizeof' is marked as a KW Expresion, instrumentation of something */ /* like 'sizeof(float) * 2' would be instrumented between the ')' and the '2'.*/ - ( ( keywords[i].kw_type & ITEM_CONSTANT ) ? 0 : ITEM_KW_EXPR ) | keywords[i].expr_type, ps, pe + 1 ) ) != NO_ERR ) + (( ( keywords[i].kw_type & ITEM_CONSTANT ) ? 0 : ITEM_KW_EXPR ) | keywords[i].expr_type), ps, pe + 1 ) ) != NO_ERR ) { goto ret; } } + /* Continue after Arguments or Value End */ ne = pe + 1; } @@ -3130,12 +3138,6 @@ static TOOL_ERROR Find_Data_Declarations( /* Skip Blanks to the Right */ ptr = Skip_Chars( start, BLANK_CHARS, ParseTbl_ptr ); } - /* Fixed Point Macros Lacking ';'? */ - if ( ( start = memwordcmp( ptr, NON_STANDARD_FXP_MACROS_STRING ) ) != NULL ) - { /* Yes */ - /* Done */ - break; - } /* Any of the Known Data Types, Type Definition Type Modifier or Storage Class? */ if ( ( start = memwordcmp( ptr, DATA_DEF_STRING ) ) != NULL ) @@ -3342,6 +3344,7 @@ static TOOL_ERROR Find_Data_Declarations( /* Done */ break; } + /* Go to End of Statement */ /* !!!!! Incorrect when have 'int a' then 'BRANCH(1)', 'a += 2', ... on Next Line (Known Limitation)*/ if ( ( start = Goto_EOS( start, ParseTbl_ptr ) ) == NULL ) @@ -3351,11 +3354,14 @@ static TOOL_ERROR Find_Data_Declarations( ErrCode = Expected_EOS( ptr ); goto ret; } + /* Skip Blanks to the Right */ ptr = Skip_Chars( start + 1, BLANK_CHARS, ParseTbl_ptr ); } + /* Set Region End */ ParseRecord.item_end = ptr; + /* Is Region Empty? */ if ( !Is_Empty_Region( ParseRecord.item_start, ptr, ParseTbl_ptr ) ) { @@ -3365,8 +3371,10 @@ static TOOL_ERROR Find_Data_Declarations( goto ret; } } + /* Change Data Declaration Region Type */ ParseRecord.item_type = ITEM_DATA_DECL_SUB | ITEM_WARNING; + /* Go to Next Code Block */ start = Find_String( ptr, "{", ParseTbl_ptr, ITEM_ANY ); } while ( start != NULL && start < end ); @@ -4146,16 +4154,19 @@ static Item_Type Get_Call_Type( /* Not a Function Call */ return ITEM_NONE; } + /* Any of the System Functions? */ if ( memwordcmp( name_start, SYSTEM_FUNCTS_STRING ) != NULL ) { /* Yes */ return ITEM_FUNC_SYSTEM | ITEM_SKIPPED; } + /* Any of the System Allocation Functions? */ if ( memwordcmp( name_start, SYSTEM_ALLOC_FUNCTS_STRING ) != NULL ) { /* Yes */ return ITEM_FUNC_SYSTEM; } + /* Any of the Instrumented System Allocation Functions? */ if ( memwordcmp( name_start, ins_sys_alloc_funcs_string ) != NULL ) { /* Yes */ @@ -4167,36 +4178,43 @@ static Item_Type Get_Call_Type( { /* Yes */ return ITEM_FUNC_MATH; } + /* Any of the Instrumented Math Functions? */ if ( memwordcmp( name_start, ins_math_funcs_string ) != NULL ) { /* Yes */ return ITEM_FUNC_MATH | ITEM_INSTRUMENTED; } + /* Any of the Manual Counting Macros? */ if ( memwordcmp( name_start, MANUAL_COUNTING_MACROS_STRING ) != NULL ) { /* Yes */ return ITEM_FUNC_COUNTERS_MAN | ITEM_INSTRUMENTATION | ITEM_SKIPPED; } + /* Any of the Left Automatic Counting Macros? */ if ( memwordcmp( name_start, AUTO_LEFT_COUNTING_MACRO_STRING ) != NULL ) { /* Yes */ return ITEM_FUNC_COUNTERS_AUTO_L | ITEM_INSTRUMENTATION | ITEM_SKIPPED; } + /* Any of the Right Automatic Counting Macros? */ if ( memwordcmp( name_start, AUTO_RIGHT_COUNTING_MACRO_STRING ) != NULL ) { /* Yes */ return ITEM_FUNC_COUNTERS_AUTO_R | ITEM_INSTRUMENTATION | ITEM_SKIPPED; } - /* Any of the Counting Library Functions? */ - //if ( memwordcmp( name_start, COUNTLIB_FUNCTS_STRING ) != NULL ) - //{ /* Yes */ - // return ITEM_FUNC_COUNT_LIB | ITEM_SKIPPED; - //} + /* Any of the WMOPS Library Functions or Macros? */ if ( memwordcmp( name_start, WMOPS_FUNCTS_STRING) != NULL ) { /* Yes */ return ITEM_FUNC_COUNT_LIB | ITEM_SKIPPED; } + + /* Any of the BASOP Functions or Macros? */ + if (memwordcmp(name_start, BASOP_FUNCTS_STRING) != NULL) + { /* Yes */ + return ITEM_FUNC_BASOP | ITEM_INSTRUMENTATION_OFF; + } + return ITEM_FUNC_PROJECT; } @@ -4328,8 +4346,8 @@ static TOOL_ERROR Find_Calls( { goto ret; } - /* Counting Macro or Function? */ - if ( item_type & ( ITEM_FUNC_COUNTERS | ITEM_FUNC_COUNT_LIB ) ) + /* Counting Macro, Couting Function or BASOP Function? */ + if ( item_type & ( ITEM_FUNC_COUNTERS | ITEM_FUNC_COUNT_LIB | ITEM_FUNC_BASOP ) ) { /* Yes */ /* Get Optional End of Statement @@ -4670,6 +4688,7 @@ static TOOL_ERROR Instrument_Keywords( /* Get Parse Table Address (for clarity) */ ParseTbl_ptr = &ParseCtx_ptr->ParseTbl; + /* Get PROM Ops Weights (for clarity) */ prom_ops_weights_ptr = &PROM_Ops_Weights[ParseCtx_ptr->PROMOpsWeightsSet]; @@ -4695,8 +4714,8 @@ static TOOL_ERROR Instrument_Keywords( { /* No */ /* Instrument After (by default) */ ptr = ParseRec_ptr->item_end; - /* Is it a 'while' that is part of a 'do' Block? */ + /* Is it a 'while' that is part of a 'do' Block? */ /* Insert Instrumentation Character */ if ( ( ErrCode = Add_Insertion( &ParseCtx_ptr->InsertTbl, ptr, WORD_INSTRUMENT_STRING ) ) != NO_ERR ) { @@ -4725,12 +4744,14 @@ static TOOL_ERROR Instrument_Keywords( { goto ret; } + if ( ( ErrCode = Add_Insertion( &ParseCtx_ptr->InsertTbl, ParseRec_ptr->item_end, ")" ) ) != NO_ERR ) { goto ret; } + /* Add Warning */ ParseRec_ptr->item_type |= ITEM_WARNING; } @@ -4746,14 +4767,22 @@ static TOOL_ERROR Instrument_Keywords( } } - /* Count Program Memory */ - if ( item_type == ITEM_KEYWORD_FOR ) - loops++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->loop; - else if ( item_type == ITEM_KEYWORD_WHILE ) - whiles++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch * 2; - else if ( item_type & ( ITEM_KEYWORD_CONTROL | ITEM_KEYWORD_IS_JUMP ) ) - jumps++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch; + ///* Count Program Memory */ + //if ( item_type == ITEM_KEYWORD_FOR ) + // loops++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->loop; + //else if ( item_type == ITEM_KEYWORD_WHILE ) + // whiles++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch * 2; + //else if ( item_type & ( ITEM_KEYWORD_CONTROL | ITEM_KEYWORD_IS_JUMP ) ) + // jumps++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch; } + + /* Count Program Memory */ /* !!! VM: Moved to this place to count PROM size even for non-instrumented keywords */ + if ( item_type == ITEM_KEYWORD_FOR ) + loops++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->loop; + else if ( item_type == ITEM_KEYWORD_WHILE ) + whiles++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch * 2; + else if ( item_type & ( ITEM_KEYWORD_CONTROL | ITEM_KEYWORD_IS_JUMP ) ) + jumps++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch; } } @@ -5411,11 +5440,13 @@ static TOOL_ERROR Instrument_Operators( /* Erase Operator Insert Table */ OperInsTbl.Size = 0; + /* Initialize (No Memory Allocated by Default) */ OperInsTbl.MaxSize = 0; /* Get Parse Table Address (for clarity) */ ParseTbl_ptr = &ParseCtx_ptr->ParseTbl; + /* Get PROM Ops Weights (for clarity) */ prom_ops_weights_ptr = &PROM_Ops_Weights[ParseCtx_ptr->PROMOpsWeightsSet]; @@ -6408,17 +6439,19 @@ TOOL_ERROR Setup_Regions( goto ret; } - /* Find Skipped Regions */ + /* Find Non-Instrumented Regions marked with #ifdef WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP */ /* Start at Beginning */ ptr = ParseCtx_ptr->File.Data; - /* Find the Beginning of Skipped Regions */ + /* Find the Beginning of Non-Instrumented Regions */ while ( ( ptr = Find_Identifier( ptr, WMC_TOOL_SKIP_STRING, ParseTbl_ptr, ITEM_CSTE_NAME, ITEM_ENCLOSED, &idx ) ) != NULL ) { /* Get Record */ ParseRec_ptr = &ParseTbl_ptr->Data[idx]; + /* Go to Start of Next Line */ ptr = strlend( ParseRec_ptr->item_end ) + 1; - /* Find the End of Skipped Regions */ + + /* Find the End of Non-Instrumented Regions */ /* Found it? */ if ( ( end = Find_Identifier( ptr, WMC_TOOL_SKIP_STRING, ParseTbl_ptr, ITEM_PREPROC_ARGS | ITEM_PREPROC_UNDEF, ITEM_ENCLOSED, &idx ) ) == NULL ) @@ -6429,16 +6462,18 @@ TOOL_ERROR Setup_Regions( Error("Unable to find matching #undef %s!", ErrCode, WMC_TOOL_SKIP_STRING); goto ret; - /* Skipped Regon will End at EOF */ + /* Skipped Region will End at EOF */ //end = file_ptr->Data + file_ptr->Size; } else { /* Yes */ /* Go to Preprocessor Command */ idx--; + /* Get Record */ ParseRec_ptr = &ParseTbl_ptr->Data[idx]; - /* Skipped Region will End before #undef */ + + /* Non-Instrumented Region will End before #undef */ end = ParseRec_ptr->item_start; } @@ -6475,8 +6510,10 @@ TOOL_ERROR Setup_Regions( { /* Get Record */ ParseRec_ptr = &ParseTbl_ptr->Data[idx]; + /* Mark Comment as Instrumentation */ ParseRec_ptr->item_type |= ITEM_INSTRUMENTATION; + /* continue After Comment */ ptr = ParseRec_ptr->item_end; } @@ -8141,7 +8178,7 @@ TOOL_ERROR Instrument( is_function_present = 1; } - /* Check, if there are const xx[] arrays */ + /* Check, if there are any const xx[] arrays */ is_cnst_data_present = 0; if ((Find_Identifier(ParseCtx_ptr->File.Data, CONST_STRING, ParseTbl_ptr, ITEM_ANY, ITEM_FUNC_DEF | ITEM_NOT_SEARCHED | ITEM_INSTRUMENTATION_OFF)) != NULL) { @@ -8168,10 +8205,13 @@ TOOL_ERROR Instrument( /* Reset Max Function Name Length */ max_name_length = 0; + /* Reset # of Occurances */ max_name_occ = 0; + /* Start at Beginning */ FctCallRec_ptr = FctCallTbl_ptr->Data; + /* Process all Project Function Calls */ for (idx = 0; idx < FctCallTbl_ptr->Size; idx++) { @@ -8179,10 +8219,12 @@ TOOL_ERROR Instrument( name_length = FctCallRec_ptr->NameLength; if (max_name_length < name_length) max_name_length = name_length; + /* Get Max Name Occurances */ name_occ = FctCallRec_ptr->NameOcc; if (max_name_occ < name_occ) max_name_occ = name_occ; + /* Advance */ FctCallRec_ptr++; } @@ -8342,6 +8384,13 @@ TOOL_ERROR Instrument( miscs += name_occ, ParseCtx_ptr->PROMSize += name_occ * prom_ops_weights_ptr->misc; } + /* Find BASOP Functions and Count their PROM size */ + for (idx = 0; (idx = Find_Region(NULL, ParseTbl_ptr, ITEM_CALL | ITEM_FUNC_BASOP, idx)) >= 0; idx++) + { + /* By default it is assumed that all BASOP functions take one PROM word */ + ParseCtx_ptr->PROMSize++; + } + /* Insert PROM_Size_Func() function */ if (is_function_present) { @@ -8697,8 +8746,10 @@ void Print_Information( void ) fprintf(stdout, "\n Manual Macros Removed during Desintrumentation:\n" ); Print_Words( MANUAL_COUNTING_MACROS_STRING ); - fprintf(stdout, "\n WMOPS Library Functions that are not Instrumented:\n" ); - Print_Words( WMOPS_FUNCTS_STRING, true ); + fprintf(stdout, "\n BASOP Functions that are not Instrumented:\n" ); + Print_Words( BASOP_FUNCTS_STRING, true ); + fprintf(stdout, "\n WMOPS Library Functions that are not Instrumented:\n"); + Print_Words(WMOPS_FUNCTS_STRING, true); fprintf(stdout, "\n System Functions that are not Instrumented:\n" ); Print_Words( SYSTEM_FUNCTS_STRING ); fprintf(stdout, "\n System Functions that are Instrumented:\n" ); diff --git a/src/wmc_tool/constants.h b/src/wmc_tool/constants.h index 1dbd9138..76812a2e 100644 --- a/src/wmc_tool/constants.h +++ b/src/wmc_tool/constants.h @@ -98,7 +98,8 @@ "Encoder_State " #define CONST_STRING "const" #define STORAGE_STRING "extern " \ - "static " CONST_STRING " " + "static " \ + "const " #define DATA_DEF_STRING TYPES_STRING TYPE_MOD_STRING TYPEDEF_STRING STORAGE_STRING diff --git a/src/wmc_tool/parsing_defs.h b/src/wmc_tool/parsing_defs.h index ec2532d2..99949148 100644 --- a/src/wmc_tool/parsing_defs.h +++ b/src/wmc_tool/parsing_defs.h @@ -188,6 +188,7 @@ #define ITEM_FUNC_COUNTERS_MAN 0x01 #define ITEM_FUNC_COUNTERS ( ITEM_FUNC_COUNTERS_MAN | ITEM_FUNC_COUNTERS_AUTO ) #define ITEM_FUNC_COUNT_LIB 0x20 +#define ITEM_FUNC_BASOP 0x10 #define ITEM_FUNC_SYSTEM 0x40 /* Attributes for ITEM_SKIPPED */ From 55316d14135c07523eadb053767936ecda162d2f Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 3 Oct 2023 08:00:53 +0200 Subject: [PATCH 26/53] small correction in the help printout --- src/wmc_tool/wmc_tool.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wmc_tool/wmc_tool.cpp b/src/wmc_tool/wmc_tool.cpp index 0fbc89bd..ee3285cd 100644 --- a/src/wmc_tool/wmc_tool.cpp +++ b/src/wmc_tool/wmc_tool.cpp @@ -69,10 +69,10 @@ static void usage() { - Print("Usage: wmc_tool [options] filename1 filename2 ...\n\n" - "WMC tool v%s - %s\n\n" + Print("WMC tool v%s - %s\n\n" + "Usage: wmc_tool [options] filename1 filename2 ...\n\n" "Mandatory arguments:\n" - " space-separated list of filenames or directories with file mask, e.g. ./lib_enc/array*.c\n" + " space-separated list of filenames or directories with file mask, e.g. ./lib_enc/array*.c my_dir/sub_dir/*.c\n" " note: if file mask is not specified *.c is assumed by default\n\n" "Options:\n" " -h [--help]: print help\n" @@ -82,7 +82,7 @@ static void usage() " -m filename [--rom filename]: add statistics about ROM and RAM consumption\n" " note: filename shall point to a .c file containing the print_mem() function\n" " -b [--no-backup]: no backup of original files\n" - " -c dirname [--generate-wmc-files dirname]: copy wmc_auto.h and wmc_auto.c to a user-specified directory\n", + " -c dirname [--generate-wmc-files dirname]: copy wmc_auto.h and wmc_auto.c to a user-specified directory\n" " -f value [--frames-per-second value]: set the number of frames per second (default 50.0)\n\n", WMC_TOOL_VERSION_NO, VERSION_STL); From dae4383ef88ce453e6652036d63c46cc208e5b54 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 3 Oct 2023 08:43:29 +0200 Subject: [PATCH 27/53] fix warning about incorrect function declaration (missing void keyword) --- src/wmc_tool/wmc_auto_c.txt | 2 +- src/wmc_tool/wmc_auto_h.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wmc_tool/wmc_auto_c.txt b/src/wmc_tool/wmc_auto_c.txt index 62659403..48624180 100644 --- a/src/wmc_tool/wmc_auto_c.txt +++ b/src/wmc_tool/wmc_auto_c.txt @@ -2113,7 +2113,7 @@ " return ( (long) tot );\r\n" "}\r\n" "\r\n" -"long DeltaWeightedOperation()\r\n" +"long DeltaWeightedOperation( void )\r\n" "{\r\n" " long NewWOper, delta;\r\n" "\r\n" diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index ccbaca49..3957924a 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -1189,7 +1189,7 @@ "extern int call_occurred;\r\n" "\r\n" "extern long TotalWeightedOperation( void );\r\n" -"long DeltaWeightedOperation();\r\n" +"long DeltaWeightedOperation( void );\r\n" "\r\n" "void Set_BASOP_WMOPS_counter( int counterId );\r\n" "void Reset_BASOP_WMOPS_counter( void );\r\n" From bdbad60cee8eb76e6ef9ccfa0404bd91b48890d6 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 3 Oct 2023 10:11:25 +0200 Subject: [PATCH 28/53] fix incorrect reading of PROM size from .c files containing digits in their names --- src/wmc_tool/text_utils.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/wmc_tool/text_utils.cpp b/src/wmc_tool/text_utils.cpp index b7d55966..076669d1 100644 --- a/src/wmc_tool/text_utils.cpp +++ b/src/wmc_tool/text_utils.cpp @@ -556,9 +556,18 @@ int Extract_Value_From_File( { if ((ptr = strstr(line, line_keyword)) != NULL) { - while (!isdigit(*ptr++)); + /* skip the keyword */ + ptr += strlen(line_keyword); + + /* search for space followed by a number */ + while (!(isspace(ptr[0]) && isdigit(ptr[1]))) + { + ptr++; + } + fclose(fp); - return strtol(--ptr, &ptr2, 10); + + return strtol(ptr, &ptr2, 10); } } From d9f7d929e6d62be67a2e4471d9d231ac611f19d3 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 3 Oct 2023 11:39:56 +0200 Subject: [PATCH 29/53] do not count PROM in skipped regions --- src/wmc_tool/c_parser.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index bfbf24cc..bc11af0e 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -4767,7 +4767,7 @@ static TOOL_ERROR Instrument_Keywords( } } - ///* Count Program Memory */ + /* Count Program Memory */ //if ( item_type == ITEM_KEYWORD_FOR ) // loops++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->loop; //else if ( item_type == ITEM_KEYWORD_WHILE ) @@ -4777,12 +4777,15 @@ static TOOL_ERROR Instrument_Keywords( } /* Count Program Memory */ /* !!! VM: Moved to this place to count PROM size even for non-instrumented keywords */ - if ( item_type == ITEM_KEYWORD_FOR ) - loops++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->loop; - else if ( item_type == ITEM_KEYWORD_WHILE ) - whiles++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch * 2; - else if ( item_type & ( ITEM_KEYWORD_CONTROL | ITEM_KEYWORD_IS_JUMP ) ) - jumps++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch; + if ( Find_Region(ParseRec_ptr->item_start, ParseTbl_ptr, ITEM_SKIPPED) < 0 ) + { + if (item_type == ITEM_KEYWORD_FOR) + loops++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->loop; + else if (item_type == ITEM_KEYWORD_WHILE) + whiles++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch * 2; + else if (item_type & (ITEM_KEYWORD_CONTROL | ITEM_KEYWORD_IS_JUMP)) + jumps++, ParseCtx_ptr->PROMSize += prom_ops_weights_ptr->branch; + } } } From 4c842d475b95bb21d0e1e8cec16ba832c47e7297 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 3 Oct 2023 12:23:00 +0200 Subject: [PATCH 30/53] update ctest files --- src/wmc_tool/test_data/ref/main2.c | 2 +- src/wmc_tool/test_data/ref/wmc_auto.c | 2 +- src/wmc_tool/test_data/ref/wmc_auto.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wmc_tool/test_data/ref/main2.c b/src/wmc_tool/test_data/ref/main2.c index 24122646..d39d52cb 100644 --- a/src/wmc_tool/test_data/ref/main2.c +++ b/src/wmc_tool/test_data/ref/main2.c @@ -20,7 +20,7 @@ /*AddedByWMC_Tool*/} /*AddedByWMC_Tool*/ROM_Size_Lookup_Table Const_Data_PROM_Table[] = /*AddedByWMC_Tool*/{ -/*AddedByWMC_Tool*/ { "out/test*.c", 734, Get_Const_Data_Size_out_test_c }, +/*AddedByWMC_Tool*/ { "out/test*.c", 119, Get_Const_Data_Size_out_test_c }, /*AddedByWMC_Tool*/ { "", -1, NULL } /*AddedByWMC_Tool*/}; /*AddedByWMC_Tool*/#endif diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index 00c28964..fcbcb09b 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -2113,7 +2113,7 @@ long TotalWeightedOperation() return ( (long) tot ); } -long DeltaWeightedOperation() +long DeltaWeightedOperation( void ) { long NewWOper, delta; diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index be056e9c..0a13cb70 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.h +++ b/src/wmc_tool/test_data/ref/wmc_auto.h @@ -1189,7 +1189,7 @@ extern long funcid_total_wmops_at_last_call_to_else; extern int call_occurred; extern long TotalWeightedOperation( void ); -long DeltaWeightedOperation(); +long DeltaWeightedOperation( void ); void Set_BASOP_WMOPS_counter( int counterId ); void Reset_BASOP_WMOPS_counter( void ); From 48ddd4105861410aebf3586c5bead7dab9507d71 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 3 Oct 2023 12:49:22 +0200 Subject: [PATCH 31/53] print error message if #undef WMC_TOOL_SKIP is not found --- src/wmc_tool/wmc_tool.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wmc_tool/wmc_tool.cpp b/src/wmc_tool/wmc_tool.cpp index ee3285cd..d0d7df2d 100644 --- a/src/wmc_tool/wmc_tool.cpp +++ b/src/wmc_tool/wmc_tool.cpp @@ -1539,6 +1539,10 @@ int main( int argc, char *argv[] ) { fprintf(stdout, "100%% Completed Successfully!\n" ); } + else + { + Print_Error(); + } return ErrCode; } From b38ea9aaf50aaedae752374cdb3891ef5549496d Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 3 Oct 2023 15:32:03 +0200 Subject: [PATCH 32/53] make sure that PROM size is calculated and inserted in .c files where all functions are skipped with #define WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP --- src/wmc_tool/c_parser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index bc11af0e..61a1c15e 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -8395,7 +8395,8 @@ TOOL_ERROR Instrument( } /* Insert PROM_Size_Func() function */ - if (is_function_present) + //if (is_function_present) + if (ParseCtx_ptr->PROMSize > 0) { if ((ErrCode = Instrument_PROM(ParseCtx_ptr)) != NO_ERR) { From bbd83a2069489a2146b137700db593cb44c2aa6f Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 3 Oct 2023 15:39:02 +0200 Subject: [PATCH 33/53] update PROM size in ctest vectors --- src/wmc_tool/test_data/ref/main2.c | 2 +- src/wmc_tool/test_data/ref/test_basop.c | 1 + src/wmc_tool/test_data/ref/test_basop32.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wmc_tool/test_data/ref/main2.c b/src/wmc_tool/test_data/ref/main2.c index d39d52cb..e77f327c 100644 --- a/src/wmc_tool/test_data/ref/main2.c +++ b/src/wmc_tool/test_data/ref/main2.c @@ -20,7 +20,7 @@ /*AddedByWMC_Tool*/} /*AddedByWMC_Tool*/ROM_Size_Lookup_Table Const_Data_PROM_Table[] = /*AddedByWMC_Tool*/{ -/*AddedByWMC_Tool*/ { "out/test*.c", 119, Get_Const_Data_Size_out_test_c }, +/*AddedByWMC_Tool*/ { "out/test*.c", 291, Get_Const_Data_Size_out_test_c }, /*AddedByWMC_Tool*/ { "", -1, NULL } /*AddedByWMC_Tool*/}; /*AddedByWMC_Tool*/#endif diff --git a/src/wmc_tool/test_data/ref/test_basop.c b/src/wmc_tool/test_data/ref/test_basop.c index b090bf13..dd20ac35 100644 --- a/src/wmc_tool/test_data/ref/test_basop.c +++ b/src/wmc_tool/test_data/ref/test_basop.c @@ -55,3 +55,4 @@ void weight_coef_a( const Word16 *a, Word16 *b, const Word16 ght ) } #undef WMC_TOOL_SKIP +/*AddedByWMC_Tool*/ int PROM_Size_Func( test_basop ) { return 15; } diff --git a/src/wmc_tool/test_data/ref/test_basop32.c b/src/wmc_tool/test_data/ref/test_basop32.c index e0e733a2..9fcdf1e7 100644 --- a/src/wmc_tool/test_data/ref/test_basop32.c +++ b/src/wmc_tool/test_data/ref/test_basop32.c @@ -2430,3 +2430,4 @@ Word32 L_msu0 (Word32 L_var3, Word16 var1, Word16 var2) { #undef WMC_TOOL_SKIP /* end of file */ +/*AddedByWMC_Tool*/ int PROM_Size_Func( test_basop32 ) { return 157; } From 1c85faf4104247cb931c1640ae85e5a8f96ae744 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 24 Oct 2023 09:07:02 +0200 Subject: [PATCH 34/53] add missing instrumentation in L40_mac() fix incorrect pointer types in TotalWeightedOperation() --- src/wmc_tool/test_data/ref/wmc_auto.c | 20 +++++++++++++------- src/wmc_tool/wmc_auto_c.txt | 21 ++++++++++++++------- src/wmc_tool/wmc_auto_h.txt | 1 + 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index fcbcb09b..baa02f21 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -278,7 +278,7 @@ void pop_wmops( void ) tot = DeltaWeightedOperation(); ops_cnt += tot; - /* update count of current record */ + /* update count of current record */ wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt; wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt; @@ -296,6 +296,7 @@ void pop_wmops( void ) current_record = -1; } + return; } @@ -373,6 +374,7 @@ void update_wmops( void ) wmops[i].max_cnt = wmops[i].current_cnt; } + if ( wmops[i].current_cnt < wmops[i].min_cnt ) { wmops[i].min_cnt = wmops[i].current_cnt; @@ -2056,7 +2058,7 @@ int funcId_where_last_call_to_else_occurred; long funcid_total_wmops_at_last_call_to_else; int call_occurred = 1; -const BASIC_OP op_weight = { +BASIC_OP op_weight = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -2096,21 +2098,24 @@ void Set_BASOP_WMOPS_counter( int counterId ) call_occurred = 1; } +extern int32_t frame; + long TotalWeightedOperation() { int i; - const long *ptr, *ptr2; + unsigned int *ptr, *ptr2; long tot; tot = 0; - ptr = (const long *) &multiCounter[currCounter]; - ptr2 = (const long *) &op_weight; - for ( i = 0; i < ( int )( sizeof( multiCounter[currCounter] ) / sizeof( long ) ); i++ ) + ptr = (unsigned int *) &multiCounter[currCounter]; + ptr2 = (unsigned int *) &op_weight; + + for ( i = 0; i < ( int )( sizeof( multiCounter[currCounter] ) / sizeof( unsigned int ) ); i++ ) { tot += ( ( *ptr++ ) * ( *ptr2++ ) ); } - return ( (long) tot ); + return ( tot ); } long DeltaWeightedOperation( void ) @@ -2118,6 +2123,7 @@ long DeltaWeightedOperation( void ) long NewWOper, delta; NewWOper = TotalWeightedOperation(); + delta = NewWOper - wmops[currCounter].LastWOper; wmops[currCounter].LastWOper = NewWOper; diff --git a/src/wmc_tool/wmc_auto_c.txt b/src/wmc_tool/wmc_auto_c.txt index 48624180..29db27f8 100644 --- a/src/wmc_tool/wmc_auto_c.txt +++ b/src/wmc_tool/wmc_auto_c.txt @@ -278,7 +278,7 @@ " tot = DeltaWeightedOperation();\r\n" " ops_cnt += tot;\r\n" "\r\n" -" /* update count of current record */\r\n" +" /* update count of current record */\r\n" " wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt;\r\n" " wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt;\r\n" "\r\n" @@ -296,6 +296,7 @@ " current_record = -1;\r\n" " }\r\n" "\r\n" +"\r\n" " return;\r\n" "}\r\n" "\r\n" @@ -373,6 +374,7 @@ " wmops[i].max_cnt = wmops[i].current_cnt;\r\n" " }\r\n" "\r\n" +"\r\n" " if ( wmops[i].current_cnt < wmops[i].min_cnt )\r\n" " {\r\n" " wmops[i].min_cnt = wmops[i].current_cnt;\r\n" @@ -2056,7 +2058,7 @@ "long funcid_total_wmops_at_last_call_to_else;\r\n" "int call_occurred = 1;\r\n" "\r\n" -"const BASIC_OP op_weight = {\r\n" +"BASIC_OP op_weight = {\r\n" " 1, 1, 1, 1, 1,\r\n" " 1, 1, 1, 1, 1,\r\n" " 1, 1, 1, 1, 1,\r\n" @@ -2096,21 +2098,24 @@ " call_occurred = 1;\r\n" "}\r\n" "\r\n" +"extern int32_t frame;\r\n" +"\r\n" "long TotalWeightedOperation()\r\n" "{\r\n" " int i;\r\n" -" const long *ptr, *ptr2;\r\n" +" unsigned int *ptr, *ptr2;\r\n" " long tot; \r\n" "\r\n" " tot = 0;\r\n" -" ptr = (const long *) &multiCounter[currCounter];\r\n" -" ptr2 = (const long *) &op_weight;\r\n" -" for ( i = 0; i < ( int )( sizeof( multiCounter[currCounter] ) / sizeof( long ) ); i++ )\r\n" +" ptr = (unsigned int *) &multiCounter[currCounter];\r\n" +" ptr2 = (unsigned int *) &op_weight;\r\n" +"\r\n" +" for ( i = 0; i < ( int )( sizeof( multiCounter[currCounter] ) / sizeof( unsigned int ) ); i++ )\r\n" " {\r\n" " tot += ( ( *ptr++ ) * ( *ptr2++ ) );\r\n" " }\r\n" "\r\n" -" return ( (long) tot );\r\n" +" return ( tot );\r\n" "}\r\n" "\r\n" "long DeltaWeightedOperation( void )\r\n" @@ -2118,6 +2123,7 @@ " long NewWOper, delta;\r\n" "\r\n" " NewWOper = TotalWeightedOperation();\r\n" +"\r\n" " delta = NewWOper - wmops[currCounter].LastWOper;\r\n" " wmops[currCounter].LastWOper = NewWOper;\r\n" "\r\n" @@ -2144,4 +2150,5 @@ "\r\n" "#endif\r\n" "\r\n" +"\r\n" "\r\n" \ No newline at end of file diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index 3957924a..3c35fdcd 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -1438,4 +1438,5 @@ "\r\n" "#endif /* WMOPS_H */\r\n" "\r\n" +"\r\n" "\r\n" \ No newline at end of file From a8d53ec176914acbad3c8e0658418c0d405cb318 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 24 Oct 2023 13:37:00 +0200 Subject: [PATCH 35/53] Update README.md with MAC OS compile instructions --- src/wmc_tool/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/wmc_tool/README.md b/src/wmc_tool/README.md index 4180f2c9..fa67a4f7 100644 --- a/src/wmc_tool/README.md +++ b/src/wmc_tool/README.md @@ -21,6 +21,7 @@ This software is protected by copyright law and by international treaties. The s ## History 2022-10-15 First release v1.4 +2023-10-24 Update to v1.5 (fixed small errors, compilation issues, added optimizations of the source code, added instrumentation of BASOP instructions and operators) ## Authors @@ -42,6 +43,7 @@ The files `wmc_auto_h.txt` and `wmc_auto_c.txt` contain functions and definition ### Unix-based systems To build the project on Unix-based platforms invoke the following commands from the top-level directory containing the file `CMakeLists.txt`: + ``` mkdir build cd build @@ -54,6 +56,7 @@ The binary file `wmc_tool` shall be created in the top-level directory. ### Windows system To build the project on MS Windows use the `cmake` command with `-G` option specifying the target platform. For example, to build project files for 64-bit MSVC 2019, invoke the following commands from the top-level directory containing the file `CMakeLists.txt`: + ``` md build cd build @@ -63,6 +66,25 @@ msbuild wmc_tool.sln The executable file `wmc_tool.exe` shall be created in the top-level directory. Note, that it is recommended to run these commands from the `Developer Command Prompt for VS2019` opened in `Administrator` mode. This ensures that all paths to libraries including the SDK can be found by the `cmake` command. This can also be verified with the `vswhere` command. +### Mac OS X system + +To build the project on OS X patforms for both, ARM and Intel architectures, invoke the following commands from the top-level directory containing the file `CMakeLists.txt`: + +``` +mkdir build +cd build +cmake -G "Xcode" "-DCMAKE_OSX_ARCHITECTURES=x86_64;arm64" .. +cmake --build . -j +``` + +At this point the binary is not signed yet and needs the following operations before it can be executed. +- Open `wmc_tool` with Finder -> Open (an error prompt should pop-up) +- Go to System Settings -> Privacy & Security and allow the `wmc_tool` to run +- Add execution rights to `wmc_tool` with: `chmod +x wmc_tool` + +The `wmc_tool` should now be ready for execution. + + ## Testing To verify the conformance of the WMC tool it's possible to invoke the `ctest` command from the `build` directory. On Windows platforms it may be necessary to append the config type with the `-C` command-line option. If no specific config type has been specified when running the `cmake` command, then `ctest -C Debug` shall be used for testing. This runs a series of pre-defined tests using some exemplary `.c` files located in the `testv/src` folder. The instrumented files are compared with their respective references located in the `testv/ref` folder. In case of test failure it's possible to re-run the test with the `--verbose` command-line option to see the reason of failure. Note, that `ctest` uses the Python wrapper script `testv/test_wmc_tool.py` for copying the source files, running the WMC tool binary (executable) file, comparing the output to the reference and cleaning up the work. From 64eec732ea679299be3ea7ce9d3b1f4f092acd3f Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 9 Nov 2023 12:27:39 +0100 Subject: [PATCH 36/53] change default unit ion memory reports from 32b words to bytes --- src/wmc_tool/test_data/ref/wmc_auto.c | 21 ++++++++++++++++----- src/wmc_tool/test_data/ref/wmc_auto.h | 3 ++- src/wmc_tool/wmc_auto_c.txt | 21 ++++++++++++++++----- src/wmc_tool/wmc_auto_h.txt | 3 ++- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index baa02f21..0ff7786f 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -25,6 +25,7 @@ #include #endif +#include "options.h" #include "wmc_auto.h" #define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */ @@ -682,7 +683,7 @@ allocator_record *allocation_list = NULL; static int Num_Records, Max_Num_Records; static size_t Stat_Cnt_Size = USE_BYTES; -static const char *Count_Unit[] = { "bytes", "words", "words" }; +static const char *Count_Unit[] = { "bytes", "words", "words", "words" }; static int32_t wc_ram_size, wc_ram_frame; static int32_t current_heap_size; @@ -1895,7 +1896,16 @@ void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) for ( i = 0; i < nElem; i++ ) { - fprintf( stdout, "Program ROM size (%s): %d instruction words\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size ); + if ( Stat_Cnt_Size > 0 ) + { + /* words */ + fprintf( stdout, "Program ROM size (%s): %d words\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size ); + } + else + { + /* bytes */ + fprintf( stdout, "Program ROM size (%s): %d bytes\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size << Stat_Cnt_Size ); + } } for ( i = 0; i < nElem; i++ ) @@ -1984,11 +1994,12 @@ void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) mem_count_summary(); #endif - if ( Stat_Cnt_Size > 0 ) + if ( Stat_Cnt_Size == 0 ) { - fprintf( stdout, "\nNote: 1 word = %d bits\n", 8 << Stat_Cnt_Size ); - fprintf( stdout, "This is an optimistic estimate of memory consumption assuming that each variable type is stored with sizeof(type) bits\n" ); + /* bytes */ + fprintf( stdout, "\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bytes (%d bits)\n", 1 << Stat_Cnt_Size, 8 << Stat_Cnt_Size ); } + fprintf( stdout, "Note: The Data ROM size is calculated using the sizeof(type) built-in function\n" ); if ( n_items_wc_intra_frame_heap > 0 ) { diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index 0a13cb70..e2f2af4d 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.h +++ b/src/wmc_tool/test_data/ref/wmc_auto.h @@ -981,7 +981,8 @@ typedef enum { USE_BYTES = 0, USE_16BITS = 1, - USE_32BITS = 2 + USE_32BITS = 2, + USE_64BITS = 3 } Counting_Size; #if ( defined( _WIN32 ) && ( _MSC_VER <= 1800 ) && ( _MSC_VER >= 1300 ) ) diff --git a/src/wmc_tool/wmc_auto_c.txt b/src/wmc_tool/wmc_auto_c.txt index 29db27f8..9a76a24f 100644 --- a/src/wmc_tool/wmc_auto_c.txt +++ b/src/wmc_tool/wmc_auto_c.txt @@ -25,6 +25,7 @@ "#include \r\n" "#endif\r\n" "\r\n" +"#include \"options.h\"\r\n" "#include \"wmc_auto.h\"\r\n" "\r\n" "#define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */\r\n" @@ -682,7 +683,7 @@ "\r\n" "static int Num_Records, Max_Num_Records;\r\n" "static size_t Stat_Cnt_Size = USE_BYTES;\r\n" -"static const char *Count_Unit[] = { \"bytes\", \"words\", \"words\" };\r\n" +"static const char *Count_Unit[] = { \"bytes\", \"words\", \"words\", \"words\" };\r\n" "\r\n" "static int32_t wc_ram_size, wc_ram_frame;\r\n" "static int32_t current_heap_size;\r\n" @@ -1895,7 +1896,16 @@ "\r\n" " for ( i = 0; i < nElem; i++ )\r\n" " {\r\n" -" fprintf( stdout, \"Program ROM size (%s): %d instruction words\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size );\r\n" +" if ( Stat_Cnt_Size > 0 )\r\n" +" {\r\n" +" /* words */\r\n" +" fprintf( stdout, \"Program ROM size (%s): %d words\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size );\r\n" +" }\r\n" +" else\r\n" +" {\r\n" +" /* bytes */\r\n" +" fprintf( stdout, \"Program ROM size (%s): %d bytes\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size << Stat_Cnt_Size );\r\n" +" }\r\n" " }\r\n" "\r\n" " for ( i = 0; i < nElem; i++ )\r\n" @@ -1984,11 +1994,12 @@ " mem_count_summary();\r\n" "#endif\r\n" "\r\n" -" if ( Stat_Cnt_Size > 0 )\r\n" +" if ( Stat_Cnt_Size == 0 )\r\n" " {\r\n" -" fprintf( stdout, \"\\nNote: 1 word = %d bits\\n\", 8 << Stat_Cnt_Size );\r\n" -" fprintf( stdout, \"This is an optimistic estimate of memory consumption assuming that each variable type is stored with sizeof(type) bits\\n\" );\r\n" +" /* bytes */\r\n" +" fprintf( stdout, \"\\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bytes (%d bits)\\n\", 1 << Stat_Cnt_Size, 8 << Stat_Cnt_Size );\r\n" " }\r\n" +" fprintf( stdout, \"Note: The Data ROM size is calculated using the sizeof(type) built-in function\\n\" );\r\n" "\r\n" " if ( n_items_wc_intra_frame_heap > 0 )\r\n" " {\r\n" diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index 3c35fdcd..d0a6e65d 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -981,7 +981,8 @@ "{\r\n" " USE_BYTES = 0,\r\n" " USE_16BITS = 1,\r\n" -" USE_32BITS = 2\r\n" +" USE_32BITS = 2,\r\n" +" USE_64BITS = 3\r\n" "} Counting_Size;\r\n" "\r\n" "#if ( defined( _WIN32 ) && ( _MSC_VER <= 1800 ) && ( _MSC_VER >= 1300 ) )\r\n" From 135b11326faf6c28ab13afe5459937654b296f0f Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 9 Nov 2023 13:37:38 +0100 Subject: [PATCH 37/53] create changelog file --- src/wmc_tool/HISTORY.md | 69 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/wmc_tool/HISTORY.md diff --git a/src/wmc_tool/HISTORY.md b/src/wmc_tool/HISTORY.md new file mode 100644 index 00000000..7cbdbc01 --- /dev/null +++ b/src/wmc_tool/HISTORY.md @@ -0,0 +1,69 @@ +# History of the WMC tool + +## v1.0 + + * initial version of the WMC tool + +## v1.1 + + * added support for PROM Counting + * added support for Table ROM Instrumentation + * added support for stack counting + * added progress bar (0-100%) + * added support for `S("string")` macro in manually-instrumented sections + +## v1.2 + + * added Unident mode to remove unused left margin + * added automatic conversion of TABs to spaces + * added a command-line switch to disable all warnings (/dw99) + + * modified the atomatically-generated file "wmc_auto.h" to eliminate warnings when compiled with `gcc` + * removed `#pragma WMC_TOOL_MESSAGE` in the `rom_*.c` files + * removed `#pragma WMC_TOOL_MESSAGE` for manually-instrumented sections + + * changed the default PROM counting weights to those specified in "S4-110479 - FTO - On Program ROM measurement for EVS standardization.doc" + * fix of a small bug where using '/op2', '/op3' crashes the WMC tool + * added support for Windows 8 command line and Cygwin V1.7.17 and later versions + * fix for a bug where a `$("[]M[]")` was inserted at the end of some C source files when it was re-instrumented + +## v1.3 + + * break in early-return out of .each early with return false + * prevent adding unnecessary space by the functions `Move_on_Chars()` and `Skip_Chars()` + * optimize the function `Find_Region()` to improve the speef of instrumentation + * optimize the function calculating the next pointer address after hitting the search target + * order macro names, system functions, etc. in the highest priority order + * combine `strlno` and `strcno` inside `Make_Position_String()` + * optimize the usage of the function `Instrument_Names()` inside `Find_Keywords()` and `Find_Calls()` + * remove the cases where `item_link` is always NULL + +## v1.4 + + * first version uploaded to the Open-ITU/STL Github repository + * remove obsolete parts of the source code (e.g. FLC) + + * bug in PROM calculation fixed + * command-line options modified (`/ic` and `/op` removed) + * memory instrumentation (PROM, Table ROM, static RAM and stack) integrated within the WMC tool (auxiliary files `PROM_Size_xxx.c` no longer generated) + * merging and renaming of files: + ** `wmops.h` and `wmops.c` -> `wmc_auto.h` and `wmc_auto.c` + ** `memory.c` -> `wmc_auto.c` + ** `mem_count.h` and `mem_count.c` -> `wmc_auto.h` and `wmc_auto.c` + ** `wmc_auto.h` and `wmc_auto.c` are optionally generated by the WMC tool with the `./wmc_tool /cp dir_name` command-line option + * PROM consumption calculated during instrumentation and added at the end in each `.c` file + * total PROM consumption may be printed with the function `print_mem()` + * re-factoring of the Table ROM instrumentation, calcula + +## v1.5 + + * fixed compilation issues on MaC platforms + * fixed a problem where `NaN` was printed for a non-instrumented function having zero complexity + * dynamic allocation and re-allocation of all internal buffers tracking both heap and stack call trees + * introduction of `-f FRAME_PER_SECONDS` command-line option allowing the user to choose the number of frames per second (default is still 50.0) + * fixed a problem when `push_wmops()` was called twice when `WMOPS_DETAILS` was activated + * raise an error message if `#define WMC_TOOL_SKIP` is not paired with `#undef WMC_TOOL_SKIP` + * export information about all dynamic allocations/de-allocations occuring during the runtime of the codec to a `.csv` file + * added an exemplary Python script `mem_analysis.py` for graphical analysis and profiling of dynamic memory alloations based on the generated `.csv` file + * added support for counting complexity and PROM size of BASOP operations and BASOP functions within floating-point source code + \ No newline at end of file From 69f297533ecf1bbaa581e038cefcb39a15eea559 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 9 Nov 2023 13:44:55 +0100 Subject: [PATCH 38/53] formatting in HISTORY.md --- src/wmc_tool/HISTORY.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wmc_tool/HISTORY.md b/src/wmc_tool/HISTORY.md index 7cbdbc01..456511f3 100644 --- a/src/wmc_tool/HISTORY.md +++ b/src/wmc_tool/HISTORY.md @@ -16,14 +16,14 @@ * added Unident mode to remove unused left margin * added automatic conversion of TABs to spaces - * added a command-line switch to disable all warnings (/dw99) + * added a command-line switch to disable all warnings (`/dw99`) * modified the atomatically-generated file "wmc_auto.h" to eliminate warnings when compiled with `gcc` * removed `#pragma WMC_TOOL_MESSAGE` in the `rom_*.c` files * removed `#pragma WMC_TOOL_MESSAGE` for manually-instrumented sections - * changed the default PROM counting weights to those specified in "S4-110479 - FTO - On Program ROM measurement for EVS standardization.doc" - * fix of a small bug where using '/op2', '/op3' crashes the WMC tool + * changed the default PROM counting weights to those specified in [S4-110479 - FTO - On Program ROM measurement for EVS standardization.doc] ([https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_64/docs/S4-110479.zip) + * fix of a small bug where using `/op2`, `/op3` crashes the WMC tool * added support for Windows 8 command line and Cygwin V1.7.17 and later versions * fix for a bug where a `$("[]M[]")` was inserted at the end of some C source files when it was re-instrumented @@ -53,7 +53,7 @@ ** `wmc_auto.h` and `wmc_auto.c` are optionally generated by the WMC tool with the `./wmc_tool /cp dir_name` command-line option * PROM consumption calculated during instrumentation and added at the end in each `.c` file * total PROM consumption may be printed with the function `print_mem()` - * re-factoring of the Table ROM instrumentation, calcula + * re-factoring of the Table ROM instrumentation, calculation and reporting ## v1.5 From 4577fe62d80c758367a6a5ac01b20bc079df921e Mon Sep 17 00:00:00 2001 From: malenov Date: Mon, 12 Feb 2024 15:58:47 +0100 Subject: [PATCH 39/53] add inv_sqrtf() to the list of intrinsic math functions --- src/wmc_tool/c_parser.cpp | 2 +- src/wmc_tool/wmc_auto_h.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index 61a1c15e..a2095381 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -141,7 +141,7 @@ "sqr Sqr SQR " \ "square Square SQUARE " \ "sign Sign SIGN " \ - "inv_sqrt " \ + "inv_sqrt inv_sqrtf " \ "log_base_2 log2_f " \ "_round round_f " \ "_squant " \ diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index d0a6e65d..0ed1d06a 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -733,7 +733,8 @@ "/* Square Root and its Variants */\r\n" "#define sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrtf( ( x ) ) )\r\n" "/* Invert Square Root and its Variants */\r\n" -"#define inv_sqrt_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) )\r\n" +"#define inv_sqrt_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) )\r\n" +"#define inv_sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) )\r\n" "/* Others */\r\n" "#define log_base_2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log_base_2( ( x ) ) )\r\n" "#define log2_f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2_f( ( x ) ) )\r\n" From 5b3c4aa4062565ff01c91c6b44e91a7ed76de1dc Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Mon, 26 Feb 2024 10:50:16 +0100 Subject: [PATCH 40/53] update the manual --- doc/manual/wmc_tool.tex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/manual/wmc_tool.tex b/doc/manual/wmc_tool.tex index 7dbc9609..595ab125 100644 --- a/doc/manual/wmc_tool.tex +++ b/doc/manual/wmc_tool.tex @@ -56,8 +56,11 @@ \subsection{Usage} note: filename shall point to a .c file containing the print_mem() function -b [--no-backup]: no backup of original files -c dirname [--generate-wmc-files dirname]: copy wmc_auto.h and wmc_auto.c to a user-specified directory + -f value [--frames-per-second value]: set the number of frames per second (default 50.0) \end{Verbatim} +The optional commend-line parameters may be used to control either the instrumentation or the couting process. With the \verb|-i| optional argument the WMC tool prints the list of about functions that are instrumented. It doesn't run the WMC tool. The parametr \verb|-d| runs the des-instrumentation phase only. The des-instrumentation phase removes all meta-information that has been previously added to the source code with the WMC tool. When \verb|-m| argument is provided the WMC tool adds code calculating ROM and RAM memory consumption and fills the \verb|print_mem()| function to print the statistics about memopry usage. When \verb|-m| is provided the user needs to specify the used the name of the \verb|.c| file containing the \verb|print_mem()| function. The parameter \verb|-b| instructs the WMC tool not to create backup copies of the instrumented \verb|.c| files. The parameter \verb|-c| copies the control files \verb|wmc_auto.h| and \verb|wmc_auto.c| to a user-specified directory. The control files must be added to the project for calculating the complexity and memory consumption of all instrumented source files. The \verb|-f| parameter allows the users to override the default values for the number of frames per second. This value is important for the correct calculation of complexity in "WMOPS". + \subsection{Instrumentation process} \label{ch:instrumentation_process} From 71555741e5ebe5b71a815f723df48bafd2553860 Mon Sep 17 00:00:00 2001 From: Ludovic Malfait Date: Tue, 12 Mar 2024 09:56:00 +0000 Subject: [PATCH 41/53] Typos in wmc_tool.tex --- doc/manual/wmc_tool.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/wmc_tool.tex b/doc/manual/wmc_tool.tex index 595ab125..4e0994a1 100644 --- a/doc/manual/wmc_tool.tex +++ b/doc/manual/wmc_tool.tex @@ -59,7 +59,7 @@ \subsection{Usage} -f value [--frames-per-second value]: set the number of frames per second (default 50.0) \end{Verbatim} -The optional commend-line parameters may be used to control either the instrumentation or the couting process. With the \verb|-i| optional argument the WMC tool prints the list of about functions that are instrumented. It doesn't run the WMC tool. The parametr \verb|-d| runs the des-instrumentation phase only. The des-instrumentation phase removes all meta-information that has been previously added to the source code with the WMC tool. When \verb|-m| argument is provided the WMC tool adds code calculating ROM and RAM memory consumption and fills the \verb|print_mem()| function to print the statistics about memopry usage. When \verb|-m| is provided the user needs to specify the used the name of the \verb|.c| file containing the \verb|print_mem()| function. The parameter \verb|-b| instructs the WMC tool not to create backup copies of the instrumented \verb|.c| files. The parameter \verb|-c| copies the control files \verb|wmc_auto.h| and \verb|wmc_auto.c| to a user-specified directory. The control files must be added to the project for calculating the complexity and memory consumption of all instrumented source files. The \verb|-f| parameter allows the users to override the default values for the number of frames per second. This value is important for the correct calculation of complexity in "WMOPS". +The optional command-line parameters may be used to control either the instrumentation or the couting process. With the \verb|-i| optional argument the WMC tool prints the list of functions that are instrumented. It doesn't run the WMC tool. The parameter \verb|-d| runs the des-instrumentation phase only. The des-instrumentation phase removes all meta-information that has been previously added to the source code with the WMC tool. When \verb|-m| argument is provided the WMC tool adds code calculating ROM and RAM memory consumption and fills the \verb|print_mem()| function to print the statistics about memory usage. When \verb|-m| is provided the user needs to specify the name of the \verb|.c| file containing the \verb|print_mem()| function. The parameter \verb|-b| instructs the WMC tool not to create backup copies of the instrumented \verb|.c| files. The parameter \verb|-c| copies the control files \verb|wmc_auto.h| and \verb|wmc_auto.c| to a user-specified directory. The control files must be added to the project for calculating the complexity and memory consumption of all instrumented source files. The \verb|-f| parameter allows the users to override the default values for the number of frames per second. This value is important for the correct calculation of complexity in "WMOPS". \subsection{Instrumentation process} \label{ch:instrumentation_process} From 03069bd4acc0c7deee68e7ea4da181b067959196 Mon Sep 17 00:00:00 2001 From: Ludovic Malfait Date: Tue, 12 Mar 2024 10:00:06 +0000 Subject: [PATCH 42/53] Typos in README.md --- src/wmc_tool/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wmc_tool/README.md b/src/wmc_tool/README.md index fa67a4f7..4731e71b 100644 --- a/src/wmc_tool/README.md +++ b/src/wmc_tool/README.md @@ -68,7 +68,7 @@ The executable file `wmc_tool.exe` shall be created in the top-level directory. ### Mac OS X system -To build the project on OS X patforms for both, ARM and Intel architectures, invoke the following commands from the top-level directory containing the file `CMakeLists.txt`: +To build the project on OS X platforms for both, ARM and Intel architectures, invoke the following commands from the top-level directory containing the file `CMakeLists.txt`: ``` mkdir build From 08e894948bc76acbb3c12652ad84ad44936154fd Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Mar 2024 15:12:52 +0100 Subject: [PATCH 43/53] recognize "f" float variants of intrinsic mathematical functions (sinf, cosf, sqrtf, ...) --- src/wmc_tool/c_parser.cpp | 40 ++++----- src/wmc_tool/test_data/ref/wmc_auto.c | 1 + src/wmc_tool/test_data/ref/wmc_auto.h | 124 ++++++++++++------------- src/wmc_tool/wmc_auto_h.txt | 125 +++++++++++++------------- 4 files changed, 144 insertions(+), 146 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index a2095381..d3ce5a2e 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -124,29 +124,23 @@ #define SYSTEM_FUNCTS_STRING \ "printf fprintf " \ "fopen fclose fwrite fread " \ - "exit " \ - "assert " - -#define MATH_FUNCTS_STRING \ - "abs fabs labs " \ - "floor " \ - "sqrt sqrtf " \ - "pow exp " \ - "log log10 " \ - "cos sin tan " \ - "acos asin atan atan2 " \ - "cosh sinh tanh " \ - "fmod " \ - "min max Min Max MIN MAX " \ - "sqr Sqr SQR " \ - "square Square SQUARE " \ - "sign Sign SIGN " \ - "inv_sqrt inv_sqrtf " \ - "log_base_2 log2_f " \ - "_round round_f " \ - "_squant " \ - "set_min set_max " \ - "mac msu " + "assert exit " + +#define MATH_FUNCTS_STRING \ + "abs fabs fabsf labs " \ + "floor floorf " \ + "sqrt sqrtf inv_sqrt inv_sqrtf " \ + "pow powf exp expf " \ + "log logf log10 log10f log_base_2 log2 log2_f " \ + "cos cosf sin sinf tan tanf " \ + "acos acosf asin asinf atan atanf atan2 atan2f " \ + "cosh coshf sinh sinhf tanh tanhf " \ + "fmod fmodf " \ + "min max Min Max MIN MAX " \ + "sqr Sqr SQR square Square SQUARE " \ + "sign Sign SIGN " \ + "_round round_f " \ + "set_min set_max " #define BASOP_FUNCTS_STRING \ "add sub abs_s shl shr extract_h extract_l mult L_mult negate round " \ diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index 0ff7786f..5e73cb92 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -2162,3 +2162,4 @@ void Reset_BASOP_WMOPS_counter( void ) #endif + diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index e2f2af4d..d42d68ee 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.h +++ b/src/wmc_tool/test_data/ref/wmc_auto.h @@ -615,14 +615,6 @@ extern int cntr_push_pop; #endif -/* mac & msu (Non Instrumented Versions) */ -#ifndef mac -#define mac( a, b, c ) ( ( a ) + ( b ) * ( c ) ) -#endif -#ifndef mac -#define msu( a, b, c ) ( ( a ) - ( b ) * ( c ) ) -#endif - #ifndef WMOPS /* DESACTIVATE the Counting Mechanism */ #define OP_COUNT_( op, n ) @@ -691,63 +683,72 @@ static int wmc_flag_ = 0; #define MISC_( x ) ABS_( x ) /* Math Operations */ -#define abs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), abs ) -#define fabs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabs ) -#define labs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), labs ) -#define floor_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floor ) -#define sqrt_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrt ) -#define pow_ OP_COUNT_WRAPPER1_( POWER_( 1 ), pow ) -#define exp_ OP_COUNT_WRAPPER1_( POWER_( 1 ), exp ) -#define log_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log ) -#define log10_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10 ) -#define cos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cos ) -#define sin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sin ) -#define tan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tan ) -#define acos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acos ) -#define asin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asin ) -#define atan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan ) -#define atan2_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2 ) -#define cosh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosh ) -#define sinh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinh ) -#define tanh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanh ) -#define fmod_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmod ) -/* these macros use any local macros already defined */ -/* min/max and their Variants */ -#define min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), min( ( a ), ( b ) ) ) -#define max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), max( ( a ), ( b ) ) ) -#define MIN_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MIN( ( a ), ( b ) ) ) -#define MAX_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MAX( ( a ), ( b ) ) ) -#define Min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Min( ( a ), ( b ) ) ) -#define Max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Max( ( a ), ( b ) ) ) -/* Square and its Variants */ -#define sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), sqr( ( x ) ) ) -#define Sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Sqr( ( x ) ) ) -#define SQR_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQR( ( x ) ) ) -#define square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), square( ( x ) ) ) -#define Square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Square( ( x ) ) ) -#define SQUARE_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQUARE( ( x ) ) ) -/* Sign and its Variants */ -#define sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), sign( ( x ) ) ) -#define Sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), Sign( ( x ) ) ) -#define SIGN_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), SIGN( ( x ) ) ) -/* Square Root and its Variants */ -#define sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrtf( ( x ) ) ) -/* Invert Square Root and its Variants */ -#define inv_sqrt_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) ) -/* Others */ +#define abs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), abs ) +#define fabs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabs ) +#define labs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), labs ) +#define floor_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floor ) +#define floorf_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floorf ) +#define sqrt_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrt ) +#define sqrtf_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrtf ) +#define pow_ OP_COUNT_WRAPPER1_( POWER_( 1 ), pow ) +#define powf_ OP_COUNT_WRAPPER1_( POWER_( 1 ), powf ) +#define exp_ OP_COUNT_WRAPPER1_( POWER_( 1 ), exp ) +#define expf_ OP_COUNT_WRAPPER1_( POWER_( 1 ), expf ) +#define log_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log ) +#define logf_ OP_COUNT_WRAPPER1_( LOG_( 1 ), logf ) +#define log10_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10 ) +#define log10f_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10f ) +#define cos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cos ) +#define cosf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosf ) +#define sin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sin ) +#define sinf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinf ) +#define tan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tan ) +#define tanf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanf ) +#define acos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acos ) +#define acosf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acosf ) +#define asin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asin ) +#define asinf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asinf ) +#define atan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan ) +#define atanf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atanf ) +#define atan2_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2 ) +#define atan2f_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2f ) +#define cosh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosh ) +#define coshf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), coshf ) +#define sinh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinh ) +#define sinhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinhf ) +#define tanh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanh ) +#define tanhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanhf ) +#define fmod_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmod ) +#define fmodf_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmodf ) + +/* the macros below are instrumented versions of user-defined macros that might be used in the source code +/* representing some well-known and recognized mathematical operations (that are not defined in math.h) */ +/* Note: the 'wmc_flag_=wmc_flag_' is used to avoid warning: left-hand operand of comma expression has no effect with gcc */ + +#define min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), min( ( a ), ( b ) ) ) +#define max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), max( ( a ), ( b ) ) ) +#define MIN_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MIN( ( a ), ( b ) ) ) +#define MAX_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MAX( ( a ), ( b ) ) ) +#define Min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Min( ( a ), ( b ) ) ) +#define Max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Max( ( a ), ( b ) ) ) +#define sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), sqr( ( x ) ) ) +#define Sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Sqr( ( x ) ) ) +#define SQR_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQR( ( x ) ) ) +#define square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), square( ( x ) ) ) +#define Square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Square( ( x ) ) ) +#define SQUARE_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQUARE( ( x ) ) ) +#define sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), sign( ( x ) ) ) +#define Sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), Sign( ( x ) ) ) +#define SIGN_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), SIGN( ( x ) ) ) +#define inv_sqrt_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) ) +#define inv_sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrtf( ( x ) ) ) #define log_base_2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log_base_2( ( x ) ) ) +#define log2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2( ( x ) ) ) #define log2_f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2_f( ( x ) ) ) -/* The 'wmc_flag_=wmc_flag_' is Used to Avoid: "warning: left-hand operand of comma expression has no effect" - with Cygwin gcc Compiler */ -#define _round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _round( ( x ) ) ) -#define round_f_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round_f( ( x ) ) ) -#define _squant_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _squant( ( x ) ) ) -/* Set Min/Max */ +#define _round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _round( ( x ) ) ) +#define round_f_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round_f( ( x ) ) ) #define set_min_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_min( ( a ), ( b ) ) ) #define set_max_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_max( ( a ), ( b ) ) ) -/* mac & msu (Instrumented Versions) */ -#define mac_( a, b, c ) OP_COUNT_WRAPPER1_( MAC_( 1 ), mac( a, b, c ) ) -#define msu_( a, b, c ) OP_COUNT_WRAPPER1_( MAC_( 1 ), msu( a, b, c ) ) /* Functions */ #define func_( name, x ) OP_COUNT_WRAPPER1_( FUNC_( x ), name ) @@ -1440,3 +1441,4 @@ static __inline void incrGoto( void) { #endif /* WMOPS_H */ + diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index 0ed1d06a..1950492f 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -615,14 +615,6 @@ "\r\n" "#endif\r\n" "\r\n" -"/* mac & msu (Non Instrumented Versions) */\r\n" -"#ifndef mac\r\n" -"#define mac( a, b, c ) ( ( a ) + ( b ) * ( c ) )\r\n" -"#endif\r\n" -"#ifndef mac\r\n" -"#define msu( a, b, c ) ( ( a ) - ( b ) * ( c ) )\r\n" -"#endif\r\n" -"\r\n" "#ifndef WMOPS\r\n" "/* DESACTIVATE the Counting Mechanism */\r\n" "#define OP_COUNT_( op, n )\r\n" @@ -691,64 +683,73 @@ "#define MISC_( x ) ABS_( x )\r\n" "\r\n" "/* Math Operations */\r\n" -"#define abs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), abs )\r\n" -"#define fabs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabs )\r\n" -"#define labs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), labs )\r\n" -"#define floor_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floor )\r\n" -"#define sqrt_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrt )\r\n" -"#define pow_ OP_COUNT_WRAPPER1_( POWER_( 1 ), pow )\r\n" -"#define exp_ OP_COUNT_WRAPPER1_( POWER_( 1 ), exp )\r\n" -"#define log_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log )\r\n" -"#define log10_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10 )\r\n" -"#define cos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cos )\r\n" -"#define sin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sin )\r\n" -"#define tan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tan )\r\n" -"#define acos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acos )\r\n" -"#define asin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asin )\r\n" -"#define atan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan )\r\n" -"#define atan2_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2 )\r\n" -"#define cosh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosh )\r\n" -"#define sinh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinh )\r\n" -"#define tanh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanh )\r\n" -"#define fmod_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmod )\r\n" -"/* these macros use any local macros already defined */\r\n" -"/* min/max and their Variants */\r\n" -"#define min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), min( ( a ), ( b ) ) )\r\n" -"#define max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), max( ( a ), ( b ) ) )\r\n" -"#define MIN_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MIN( ( a ), ( b ) ) )\r\n" -"#define MAX_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MAX( ( a ), ( b ) ) )\r\n" -"#define Min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Min( ( a ), ( b ) ) )\r\n" -"#define Max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Max( ( a ), ( b ) ) )\r\n" -"/* Square and its Variants */\r\n" -"#define sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), sqr( ( x ) ) )\r\n" -"#define Sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Sqr( ( x ) ) )\r\n" -"#define SQR_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQR( ( x ) ) )\r\n" -"#define square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), square( ( x ) ) )\r\n" -"#define Square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Square( ( x ) ) )\r\n" -"#define SQUARE_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQUARE( ( x ) ) )\r\n" -"/* Sign and its Variants */\r\n" -"#define sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), sign( ( x ) ) )\r\n" -"#define Sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), Sign( ( x ) ) )\r\n" -"#define SIGN_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), SIGN( ( x ) ) )\r\n" -"/* Square Root and its Variants */\r\n" -"#define sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrtf( ( x ) ) )\r\n" -"/* Invert Square Root and its Variants */\r\n" -"#define inv_sqrt_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) )\r\n" -"#define inv_sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) )\r\n" -"/* Others */\r\n" +"#define abs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), abs )\r\n" +"#define fabs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabs )\r\n" +"#define fabsf_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabsf )\r\n" +"#define labs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), labs )\r\n" +"#define floor_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floor )\r\n" +"#define floorf_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floorf )\r\n" +"#define sqrt_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrt )\r\n" +"#define sqrtf_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrtf )\r\n" +"#define pow_ OP_COUNT_WRAPPER1_( POWER_( 1 ), pow )\r\n" +"#define powf_ OP_COUNT_WRAPPER1_( POWER_( 1 ), powf )\r\n" +"#define exp_ OP_COUNT_WRAPPER1_( POWER_( 1 ), exp )\r\n" +"#define expf_ OP_COUNT_WRAPPER1_( POWER_( 1 ), expf )\r\n" +"#define log_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log )\r\n" +"#define logf_ OP_COUNT_WRAPPER1_( LOG_( 1 ), logf )\r\n" +"#define log10_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10 )\r\n" +"#define log10f_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10f )\r\n" +"#define cos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cos )\r\n" +"#define cosf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosf )\r\n" +"#define sin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sin )\r\n" +"#define sinf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinf )\r\n" +"#define tan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tan )\r\n" +"#define tanf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanf )\r\n" +"#define acos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acos )\r\n" +"#define acosf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acosf )\r\n" +"#define asin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asin )\r\n" +"#define asinf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asinf )\r\n" +"#define atan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan )\r\n" +"#define atanf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atanf )\r\n" +"#define atan2_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2 )\r\n" +"#define atan2f_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2f )\r\n" +"#define cosh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosh )\r\n" +"#define coshf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), coshf )\r\n" +"#define sinh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinh )\r\n" +"#define sinhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinhf )\r\n" +"#define tanh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanh )\r\n" +"#define tanhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanhf )\r\n" +"#define fmod_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmod )\r\n" +"#define fmodf_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmodf )\r\n" +"\r\n" +"/* the macros below are instrumented versions of user-defined macros that might be used in the source code \r\n" +"/* representing some well-known and recognized mathematical operations (that are not defined in math.h) */\r\n" +"/* Note: the 'wmc_flag_=wmc_flag_' is used to avoid warning: left-hand operand of comma expression has no effect with gcc */\r\n" +"\r\n" +"#define min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), min( ( a ), ( b ) ) )\r\n" +"#define max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), max( ( a ), ( b ) ) )\r\n" +"#define MIN_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MIN( ( a ), ( b ) ) )\r\n" +"#define MAX_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MAX( ( a ), ( b ) ) )\r\n" +"#define Min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Min( ( a ), ( b ) ) )\r\n" +"#define Max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Max( ( a ), ( b ) ) )\r\n" +"#define sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), sqr( ( x ) ) )\r\n" +"#define Sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Sqr( ( x ) ) )\r\n" +"#define SQR_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQR( ( x ) ) )\r\n" +"#define square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), square( ( x ) ) )\r\n" +"#define Square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Square( ( x ) ) )\r\n" +"#define SQUARE_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQUARE( ( x ) ) )\r\n" +"#define sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), sign( ( x ) ) )\r\n" +"#define Sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), Sign( ( x ) ) )\r\n" +"#define SIGN_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), SIGN( ( x ) ) )\r\n" +"#define inv_sqrt_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) )\r\n" +"#define inv_sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrtf( ( x ) ) )\r\n" "#define log_base_2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log_base_2( ( x ) ) )\r\n" +"#define log2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2( ( x ) ) )\r\n" "#define log2_f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2_f( ( x ) ) )\r\n" -"/* The 'wmc_flag_=wmc_flag_' is Used to Avoid: \"warning: left-hand operand of comma expression has no effect\"\r\n" -" with Cygwin gcc Compiler */\r\n" -"#define _round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _round( ( x ) ) )\r\n" -"#define round_f_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round_f( ( x ) ) )\r\n" -"#define _squant_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _squant( ( x ) ) )\r\n" -"/* Set Min/Max */\r\n" +"#define _round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _round( ( x ) ) )\r\n" +"#define round_f_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round_f( ( x ) ) )\r\n" "#define set_min_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_min( ( a ), ( b ) ) )\r\n" "#define set_max_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_max( ( a ), ( b ) ) )\r\n" -"/* mac & msu (Instrumented Versions) */\r\n" -"#define mac_( a, b, c ) OP_COUNT_WRAPPER1_( MAC_( 1 ), mac( a, b, c ) )\r\n" -"#define msu_( a, b, c ) OP_COUNT_WRAPPER1_( MAC_( 1 ), msu( a, b, c ) )\r\n" "\r\n" "/* Functions */\r\n" "#define func_( name, x ) OP_COUNT_WRAPPER1_( FUNC_( x ), name )\r\n" From 7720fc1090abc5986675c9e3c2fe9537870e3ef8 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 13 Mar 2024 15:13:11 +0100 Subject: [PATCH 44/53] add Python requirements file --- src/wmc_tool/requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/wmc_tool/requirements.txt diff --git a/src/wmc_tool/requirements.txt b/src/wmc_tool/requirements.txt new file mode 100644 index 00000000..510bec35 --- /dev/null +++ b/src/wmc_tool/requirements.txt @@ -0,0 +1,3 @@ +matplotlib>=3.8 +numpy>=1.26 +pandas>=2.2 From e5ab397b01ff77bb01d28dc66647374d2b1c98f7 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Wed, 13 Mar 2024 15:51:51 +0100 Subject: [PATCH 45/53] update helper files --- src/wmc_tool/test_data/ref/wmc_auto.c | 15 ++++++++--- src/wmc_tool/test_data/ref/wmc_auto.h | 37 +++++++++++++-------------- src/wmc_tool/wmc_auto_c.txt | 15 ++++++++--- src/wmc_tool/wmc_auto_h.txt | 36 ++++++++++++-------------- 4 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/wmc_tool/test_data/ref/wmc_auto.c b/src/wmc_tool/test_data/ref/wmc_auto.c index 5e73cb92..7dfcf8bf 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.c +++ b/src/wmc_tool/test_data/ref/wmc_auto.c @@ -42,6 +42,8 @@ #define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of records, increase the number of records by this number */ #define MAX_CALL_TREE_DEPTH 100 /* maximum depth of the function call tree */ #define DOUBLE_MAX 0x80000000 +#define FAC ( FRAMES_PER_SECOND / 1e6 ) + typedef struct { @@ -1903,8 +1905,8 @@ void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) } else { - /* bytes */ - fprintf( stdout, "Program ROM size (%s): %d bytes\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size << Stat_Cnt_Size ); + /* bytes (here, we assume that each instruction takes PROM_INST_SIZE bits of the PROM memory) */ + fprintf( stdout, "Program ROM size (%s): %d bytes\n", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size * ( PROM_INST_SIZE / 8 ) ); } } @@ -1994,10 +1996,15 @@ void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] ) mem_count_summary(); #endif - if ( Stat_Cnt_Size == 0 ) + if ( Stat_Cnt_Size > 0 ) + { + /* words */ + fprintf( stdout, "\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bits\n", 8 << Stat_Cnt_Size ); + } + else { /* bytes */ - fprintf( stdout, "\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bytes (%d bits)\n", 1 << Stat_Cnt_Size, 8 << Stat_Cnt_Size ); + fprintf( stdout, "\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bits\n", PROM_INST_SIZE ); } fprintf( stdout, "Note: The Data ROM size is calculated using the sizeof(type) built-in function\n" ); diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index d42d68ee..3b47ceea 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.h +++ b/src/wmc_tool/test_data/ref/wmc_auto.h @@ -33,10 +33,8 @@ #define INT_MAX 32767 #endif -/* Real-time relationships */ #define FRAMES_PER_SECOND 50.0 -#define WMOPS_BOOST_FAC ( 1.0f ) /* scaling factor for equalizing the difference between automatic and manual instrumentation */ -#define FAC ( FRAMES_PER_SECOND / 1e6 * WMOPS_BOOST_FAC ) +#define PROM_INST_SIZE 32 /* number of bits of each program instruction when stored in the PROM memory (applied only when the user selects reporting in bytes) */ #ifdef WMOPS enum instructions @@ -662,29 +660,30 @@ static int wmc_flag_ = 0; #endif /* Define all Macros without '{' & '}' (None of these should be called externally!) */ -#define ABS_( x ) OP_COUNT_( _ABS, ( x ) / WMOPS_BOOST_FAC ) -#define ADD_( x ) OP_COUNT_( _ADD, ( x ) / WMOPS_BOOST_FAC ) -#define MULT_( x ) OP_COUNT_( _MULT, ( x ) / WMOPS_BOOST_FAC ) -#define MAC_( x ) OP_COUNT_( _MAC, ( x ) / WMOPS_BOOST_FAC ) -#define MOVE_( x ) OP_COUNT_( _MOVE, ( x ) / WMOPS_BOOST_FAC ) -#define STORE_( x ) OP_COUNT_( _STORE, ( x ) / WMOPS_BOOST_FAC ) -#define LOGIC_( x ) OP_COUNT_( _LOGIC, ( x ) / WMOPS_BOOST_FAC ) -#define SHIFT_( x ) OP_COUNT_( _SHIFT, ( x ) / WMOPS_BOOST_FAC ) -#define BRANCH_( x ) OP_COUNT_( _BRANCH, ( x ) / WMOPS_BOOST_FAC ) -#define DIV_( x ) OP_COUNT_( _DIV, ( x ) / WMOPS_BOOST_FAC ) -#define SQRT_( x ) OP_COUNT_( _SQRT, ( x ) / WMOPS_BOOST_FAC ) -#define TRANS_( x ) OP_COUNT_( _TRANS, ( x ) / WMOPS_BOOST_FAC ) +#define ABS_( x ) OP_COUNT_( _ABS, ( x ) ) +#define ADD_( x ) OP_COUNT_( _ADD, ( x ) ) +#define MULT_( x ) OP_COUNT_( _MULT, ( x ) ) +#define MAC_( x ) OP_COUNT_( _MAC, ( x ) ) +#define MOVE_( x ) OP_COUNT_( _MOVE, ( x ) ) +#define STORE_( x ) OP_COUNT_( _STORE, ( x ) ) +#define LOGIC_( x ) OP_COUNT_( _LOGIC, ( x ) ) +#define SHIFT_( x ) OP_COUNT_( _SHIFT, ( x ) ) +#define BRANCH_( x ) OP_COUNT_( _BRANCH, ( x ) ) +#define DIV_( x ) OP_COUNT_( _DIV, ( x ) ) +#define SQRT_( x ) OP_COUNT_( _SQRT, ( x ) ) +#define TRANS_( x ) OP_COUNT_( _TRANS, ( x ) ) #define POWER_( x ) TRANS_( x ) #define LOG_( x ) TRANS_( x ) -#define LOOP_( x ) OP_COUNT_( _LOOP, ( x ) / WMOPS_BOOST_FAC ) -#define INDIRECT_( x ) OP_COUNT_( _INDIRECT, ( x ) / WMOPS_BOOST_FAC ) -#define PTR_INIT_( x ) OP_COUNT_( _PTR_INIT, ( x ) / WMOPS_BOOST_FAC ) -#define FUNC_( x ) ( OP_COUNT_( _MOVE, ( x ) / WMOPS_BOOST_FAC ), OP_COUNT_( _FUNC, 1 ) ) +#define LOOP_( x ) OP_COUNT_( _LOOP, ( x ) ) +#define INDIRECT_( x ) OP_COUNT_( _INDIRECT, ( x ) ) +#define PTR_INIT_( x ) OP_COUNT_( _PTR_INIT, ( x ) ) +#define FUNC_( x ) ( OP_COUNT_( _MOVE, ( x ) ), OP_COUNT_( _FUNC, 1 ) ) #define MISC_( x ) ABS_( x ) /* Math Operations */ #define abs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), abs ) #define fabs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabs ) +#define fabsf_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabsf ) #define labs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), labs ) #define floor_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floor ) #define floorf_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floorf ) diff --git a/src/wmc_tool/wmc_auto_c.txt b/src/wmc_tool/wmc_auto_c.txt index 9a76a24f..7d78d114 100644 --- a/src/wmc_tool/wmc_auto_c.txt +++ b/src/wmc_tool/wmc_auto_c.txt @@ -42,6 +42,8 @@ "#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of records, increase the number of records by this number */\r\n" "#define MAX_CALL_TREE_DEPTH 100 /* maximum depth of the function call tree */\r\n" "#define DOUBLE_MAX 0x80000000\r\n" +"#define FAC ( FRAMES_PER_SECOND / 1e6 )\r\n" +"\r\n" "\r\n" "typedef struct \r\n" "{\r\n" @@ -1903,8 +1905,8 @@ " }\r\n" " else\r\n" " {\r\n" -" /* bytes */\r\n" -" fprintf( stdout, \"Program ROM size (%s): %d bytes\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size << Stat_Cnt_Size );\r\n" +" /* bytes (here, we assume that each instruction takes PROM_INST_SIZE bits of the PROM memory) */\r\n" +" fprintf( stdout, \"Program ROM size (%s): %d bytes\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size * ( PROM_INST_SIZE / 8 ) );\r\n" " }\r\n" " }\r\n" "\r\n" @@ -1994,10 +1996,15 @@ " mem_count_summary();\r\n" "#endif\r\n" "\r\n" -" if ( Stat_Cnt_Size == 0 )\r\n" +" if ( Stat_Cnt_Size > 0 )\r\n" +" {\r\n" +" /* words */\r\n" +" fprintf( stdout, \"\\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bits\\n\", 8 << Stat_Cnt_Size );\r\n" +" }\r\n" +" else\r\n" " {\r\n" " /* bytes */\r\n" -" fprintf( stdout, \"\\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bytes (%d bits)\\n\", 1 << Stat_Cnt_Size, 8 << Stat_Cnt_Size );\r\n" +" fprintf( stdout, \"\\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bits\\n\", PROM_INST_SIZE );\r\n" " }\r\n" " fprintf( stdout, \"Note: The Data ROM size is calculated using the sizeof(type) built-in function\\n\" );\r\n" "\r\n" diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index 1950492f..222e5540 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -33,10 +33,8 @@ "#define INT_MAX 32767\r\n" "#endif\r\n" "\r\n" -"/* Real-time relationships */\r\n" "#define FRAMES_PER_SECOND 50.0 \r\n" -"#define WMOPS_BOOST_FAC ( 1.0f ) /* scaling factor for equalizing the difference between automatic and manual instrumentation */\r\n" -"#define FAC ( FRAMES_PER_SECOND / 1e6 * WMOPS_BOOST_FAC )\r\n" +"#define PROM_INST_SIZE 32 /* number of bits of each program instruction when stored in the PROM memory (applied only when the user selects reporting in bytes) */\r\n" "\r\n" "#ifdef WMOPS\r\n" "enum instructions\r\n" @@ -662,24 +660,24 @@ "#endif\r\n" "\r\n" "/* Define all Macros without '{' & '}' (None of these should be called externally!) */\r\n" -"#define ABS_( x ) OP_COUNT_( _ABS, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define ADD_( x ) OP_COUNT_( _ADD, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define MULT_( x ) OP_COUNT_( _MULT, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define MAC_( x ) OP_COUNT_( _MAC, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define MOVE_( x ) OP_COUNT_( _MOVE, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define STORE_( x ) OP_COUNT_( _STORE, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define LOGIC_( x ) OP_COUNT_( _LOGIC, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define SHIFT_( x ) OP_COUNT_( _SHIFT, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define BRANCH_( x ) OP_COUNT_( _BRANCH, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define DIV_( x ) OP_COUNT_( _DIV, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define SQRT_( x ) OP_COUNT_( _SQRT, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define TRANS_( x ) OP_COUNT_( _TRANS, ( x ) / WMOPS_BOOST_FAC )\r\n" +"#define ABS_( x ) OP_COUNT_( _ABS, ( x ) )\r\n" +"#define ADD_( x ) OP_COUNT_( _ADD, ( x ) )\r\n" +"#define MULT_( x ) OP_COUNT_( _MULT, ( x ) )\r\n" +"#define MAC_( x ) OP_COUNT_( _MAC, ( x ) )\r\n" +"#define MOVE_( x ) OP_COUNT_( _MOVE, ( x ) )\r\n" +"#define STORE_( x ) OP_COUNT_( _STORE, ( x ) )\r\n" +"#define LOGIC_( x ) OP_COUNT_( _LOGIC, ( x ) )\r\n" +"#define SHIFT_( x ) OP_COUNT_( _SHIFT, ( x ) )\r\n" +"#define BRANCH_( x ) OP_COUNT_( _BRANCH, ( x ) )\r\n" +"#define DIV_( x ) OP_COUNT_( _DIV, ( x ) )\r\n" +"#define SQRT_( x ) OP_COUNT_( _SQRT, ( x ) )\r\n" +"#define TRANS_( x ) OP_COUNT_( _TRANS, ( x ) )\r\n" "#define POWER_( x ) TRANS_( x )\r\n" "#define LOG_( x ) TRANS_( x )\r\n" -"#define LOOP_( x ) OP_COUNT_( _LOOP, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define INDIRECT_( x ) OP_COUNT_( _INDIRECT, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define PTR_INIT_( x ) OP_COUNT_( _PTR_INIT, ( x ) / WMOPS_BOOST_FAC )\r\n" -"#define FUNC_( x ) ( OP_COUNT_( _MOVE, ( x ) / WMOPS_BOOST_FAC ), OP_COUNT_( _FUNC, 1 ) )\r\n" +"#define LOOP_( x ) OP_COUNT_( _LOOP, ( x ) )\r\n" +"#define INDIRECT_( x ) OP_COUNT_( _INDIRECT, ( x ) )\r\n" +"#define PTR_INIT_( x ) OP_COUNT_( _PTR_INIT, ( x ) )\r\n" +"#define FUNC_( x ) ( OP_COUNT_( _MOVE, ( x ) ), OP_COUNT_( _FUNC, 1 ) )\r\n" "#define MISC_( x ) ABS_( x )\r\n" "\r\n" "/* Math Operations */\r\n" From ab4c8b774b7b6cbda9b72aa3f141a23aedd1ecca Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Thu, 14 Mar 2024 13:31:17 +0100 Subject: [PATCH 46/53] add support for frexp() and frexpf() functions at the cost of MISC(2) --- src/wmc_tool/c_parser.cpp | 2 +- src/wmc_tool/test_data/ref/wmc_auto.h | 2 ++ src/wmc_tool/wmc_auto_h.txt | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index d3ce5a2e..b207c3ca 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -135,7 +135,7 @@ "cos cosf sin sinf tan tanf " \ "acos acosf asin asinf atan atanf atan2 atan2f " \ "cosh coshf sinh sinhf tanh tanhf " \ - "fmod fmodf " \ + "fmod fmodf frexp frexpf " \ "min max Min Max MIN MAX " \ "sqr Sqr SQR square Square SQUARE " \ "sign Sign SIGN " \ diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index 3b47ceea..41e09b89 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.h +++ b/src/wmc_tool/test_data/ref/wmc_auto.h @@ -719,6 +719,8 @@ static int wmc_flag_ = 0; #define tanhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanhf ) #define fmod_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmod ) #define fmodf_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmodf ) +#define frexp_ OP_COUNT_WRAPPER1_( MISC_( 2 ), frexp ) +#define frexpf_ OP_COUNT_WRAPPER1_( MISC_( 2 ), frexpf ) /* the macros below are instrumented versions of user-defined macros that might be used in the source code /* representing some well-known and recognized mathematical operations (that are not defined in math.h) */ diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index 222e5540..c05c23d8 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -719,6 +719,8 @@ "#define tanhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanhf )\r\n" "#define fmod_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmod )\r\n" "#define fmodf_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmodf )\r\n" +"#define frexp_ OP_COUNT_WRAPPER1_( MISC_( 2 ), frexp )\r\n" +"#define frexpf_ OP_COUNT_WRAPPER1_( MISC_( 2 ), frexpf )\r\n" "\r\n" "/* the macros below are instrumented versions of user-defined macros that might be used in the source code \r\n" "/* representing some well-known and recognized mathematical operations (that are not defined in math.h) */\r\n" From 85add9247547e77b639a2d7329233b0bee277c74 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 19 Mar 2024 08:47:20 +0100 Subject: [PATCH 47/53] include the variants of log2() and round() functions --- src/wmc_tool/c_parser.cpp | 38 +++++++++++++-------------- src/wmc_tool/test_data/ref/wmc_auto.h | 3 +++ src/wmc_tool/wmc_auto_h.txt | 3 +++ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index b207c3ca..0a4a5e2a 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -121,25 +121,25 @@ #define SYSTEM_ALLOC_FUNCTS_STRING \ "malloc calloc free " -#define SYSTEM_FUNCTS_STRING \ - "printf fprintf " \ - "fopen fclose fwrite fread " \ - "assert exit " - -#define MATH_FUNCTS_STRING \ - "abs fabs fabsf labs " \ - "floor floorf " \ - "sqrt sqrtf inv_sqrt inv_sqrtf " \ - "pow powf exp expf " \ - "log logf log10 log10f log_base_2 log2 log2_f " \ - "cos cosf sin sinf tan tanf " \ - "acos acosf asin asinf atan atanf atan2 atan2f " \ - "cosh coshf sinh sinhf tanh tanhf " \ - "fmod fmodf frexp frexpf " \ - "min max Min Max MIN MAX " \ - "sqr Sqr SQR square Square SQUARE " \ - "sign Sign SIGN " \ - "_round round_f " \ +#define SYSTEM_FUNCTS_STRING \ + "printf fprintf " \ + "fopen fclose fwrite fread " \ + "assert exit " + +#define MATH_FUNCTS_STRING \ + "abs fabs fabsf labs " \ + "floor floorf " \ + "sqrt sqrtf inv_sqrt inv_sqrtf " \ + "pow powf exp expf " \ + "log logf log10 log10f log_base_2 log2 log2f log2_f " \ + "cos cosf sin sinf tan tanf " \ + "acos acosf asin asinf atan atanf atan2 atan2f " \ + "cosh coshf sinh sinhf tanh tanhf " \ + "fmod fmodf frexp frexpf " \ + "min max Min Max MIN MAX " \ + "sqr Sqr SQR square Square SQUARE " \ + "sign Sign SIGN " \ + "_round round round_f roundf " \ "set_min set_max " #define BASOP_FUNCTS_STRING \ diff --git a/src/wmc_tool/test_data/ref/wmc_auto.h b/src/wmc_tool/test_data/ref/wmc_auto.h index 41e09b89..3dae42b6 100644 --- a/src/wmc_tool/test_data/ref/wmc_auto.h +++ b/src/wmc_tool/test_data/ref/wmc_auto.h @@ -745,9 +745,12 @@ static int wmc_flag_ = 0; #define inv_sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrtf( ( x ) ) ) #define log_base_2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log_base_2( ( x ) ) ) #define log2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2( ( x ) ) ) +#define log2f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2f( ( x ) ) ) #define log2_f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2_f( ( x ) ) ) #define _round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _round( ( x ) ) ) +#define round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round( ( x ) ) ) #define round_f_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round_f( ( x ) ) ) +#define roundf_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, roundf( ( x ) ) ) #define set_min_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_min( ( a ), ( b ) ) ) #define set_max_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_max( ( a ), ( b ) ) ) diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index c05c23d8..e8d3c332 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -745,9 +745,12 @@ "#define inv_sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrtf( ( x ) ) )\r\n" "#define log_base_2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log_base_2( ( x ) ) )\r\n" "#define log2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2( ( x ) ) )\r\n" +"#define log2f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2f( ( x ) ) )\r\n" "#define log2_f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2_f( ( x ) ) )\r\n" "#define _round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _round( ( x ) ) )\r\n" +"#define round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round( ( x ) ) )\r\n" "#define round_f_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round_f( ( x ) ) )\r\n" +"#define roundf_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, roundf( ( x ) ) )\r\n" "#define set_min_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_min( ( a ), ( b ) ) )\r\n" "#define set_max_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_max( ( a ), ( b ) ) )\r\n" "\r\n" From 4d1a93c17aa711fba739b05c0df27fa299d17aa0 Mon Sep 17 00:00:00 2001 From: malenov Date: Tue, 19 Mar 2024 09:34:37 +0100 Subject: [PATCH 48/53] Update wmc_tool.tex add explanation about where to place the push_wmops() function calls --- doc/manual/wmc_tool.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/wmc_tool.tex b/doc/manual/wmc_tool.tex index 4e0994a1..2274b7de 100644 --- a/doc/manual/wmc_tool.tex +++ b/doc/manual/wmc_tool.tex @@ -438,7 +438,7 @@ \subsection{Complexity of functions and functional blocks} } \end{Verbatim} -The string inside each \verb|push_wmops()| macro shall be unique in the entire project and reflect what is being measured. It does not need to be the same as the name of the function in which it is used. The macro \verb|pop_wmops()| must \textbf{ALWAYS} be used to terminate the complexity counters associated with each \verb|push_wmops()| macro. The WMC tool supports nesting of these macro pairs. Note, that the \verb|push_wmops()| and the \verb|pop_wmops()| macros do not need to be encapsulated within the \verb|#define WMC_TOOL_SKIP| \ldots \verb|#undef WMC_TOOL_SKIP| macro pairs. +The \verb|push_wmops()| macro must be placed after the declaration of local variables. The string inside each \verb|push_wmops()| macro shall be unique in the entire project and reflect what is being measured. It does not need to be the same as the name of the function in which it is used. The macro \verb|pop_wmops()| must \textbf{ALWAYS} be used to terminate the complexity counters associated with each \verb|push_wmops()| macro. The WMC tool supports nesting of these macro pairs. Note, that the \verb|push_wmops()| and the \verb|pop_wmops()| macros do not need to be encapsulated within the \verb|#define WMC_TOOL_SKIP| \ldots \verb|#undef WMC_TOOL_SKIP| macro pairs. %----------------------------------------------------------------------------- \subsection{Printing statistics about computational complexity} From 9bf35436d30a71950e3702ca0effae807c49f94c Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Wed, 20 Mar 2024 10:46:11 +0100 Subject: [PATCH 49/53] update the WMC tool manual with additional info on how to format the source code --- doc/manual/wmc_tool.tex | 60 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/doc/manual/wmc_tool.tex b/doc/manual/wmc_tool.tex index 4e0994a1..919ca120 100644 --- a/doc/manual/wmc_tool.tex +++ b/doc/manual/wmc_tool.tex @@ -110,7 +110,9 @@ \subsubsection{Warnings} \section{Instrumentation of complexity} \label{ch:instrumentation_of_complexity} -The WMC tool only instruments functions but not macros. System functions and functions with names that are considered as basic operations are skipped. The instrumentation mechanism has been designed to be as least intrusive as possible. The instrumentation code is inserted at the beginning of each instrumented line in the source code with a single macro that has the following form \verb|$("ops")| where \verb|ops| is a string of letters and symbols indicating individual operations. The WMC tool parses each source file and calculates the length of each instrumentation string. The maximum length is then used to indent the entire source file to make space for the instrumentation strings. This is shown in the example below. +The instrumentation mechanism has been designed to minimize intrusiveness. It skips system functions and functions labeled as basic operations. When instrumenting \verb|.c| source files, the WMC tool omits macros by default. However, it detects macros defined within the same \verb|.c| source file using the \verb|#define| pragma. Macros defined in external header files and included using \verb|#include| pragma cannot be directly detected. To address this, the instrumentation mechanism employs a heuristic: if a string contains more than 70\% capital letters, it's treated as a macro and skipped during instrumentation. + +The instrumentation code is integrated at the start of every instrumented line in the source code using a singular macro format: \verb|$("ops")|, where \verb|ops| represents a string encompassing individual operations denoted by letters and symbols. Upon parsing each source file, the WMC tool computes the length of each instrumentation string. Subsequently, the maximum length determines the indentation of the entire source file to accommodate the instrumentation strings, as illustrated in the example below. \begin{Verbatim}[fontsize=\small] float calc_shift_value( const float totalStep ) @@ -128,13 +130,13 @@ \section{Instrumentation of complexity} } \end{Verbatim} -It is always possible to remove the instrumentation from the source code with the \verb|-d| command line option. In many cases, the source file will then return to its original non-instrumented state except for whitespace characters and text alignment. The un-instrumentation process is also invoked automatically by the WMC tool as the first step in the instrumentation process. This ensures that the results are identical even when the process is repeated multiple times. This allows the users to make modifications to an already instrumented source code by simply re-instrumenting it again. Note, that the un-instrumentation process does not remove the functions \verb|push_wmops()| and \verb|pop_wmops()| and the system functions. Lines that are preceded by +The removal of instrumentation from the source code can be achieved using the \verb|-d| command line option. In many instances, executing this option restores the source file to its original non-instrumented state, with the exception of whitespace characters and text alignment. The un-instrumentation process is automatically triggered by the WMC tool as the initial step in the instrumentation procedure. This ensures consistent results even with repeated executions. Consequently, users can modify previously instrumented source code by simply re-instrumenting it. It's important to note that the un-instrumentation process does not eliminate the \verb|push_wmops()| and \verb|pop_wmops()| functions, nor does it remove system functions. Lines preceded by { {\em /*AddedByWMC\_Tool*/} } -are removed completely. At the end of the un-instrumentation process, the original indentation is restored. +are removed completely. At the end of the un-instrumentation process the original indentation is restored. If, for any reason, it is necessary to avoid the automatic instrumentation and un-instrumentation of certain parts in the source code, the user may encapsulate such parts in \verb|#define WMC_TOOL_SKIP| \ldots \verb|#undef WMC_TOOL_SKIP| macro pairs. For example @@ -227,18 +229,21 @@ \subsection{Mathematic functions} \verb|sign()| & 1 \\ \verb|round()| & 0 (not supported by the WMC tool) \\ \verb|sqrt()| and \verb|inv_sqrt()| & 10 \\ -\verb|log()|, \verb|log2()| and thier derivatives & 25 \\ +\verb|log()|, \verb|log2()| and other logarithmic functions & 25 \\ \verb|exp()| and \verb|pow()| & 25 \\ \verb|sin()|, \verb|cos()| and all other trigonometric functions & 25 \\ +\verb|frexp()| & 2 \\ +\verb|fmod()| & 18 \\ \hline \end{tabular} \label{tab:cost_of_math_functions} \end{table} + %----------------------------------------------------------------------------- \subsection{User-defined functions} -All user-defined functions in the source code are instrumented with one or more underscore '\verb|_|' symbols appended to their names. The WMC tool will count the number of arguments from the function call and insert a wrapper macro at the top of the source file, after the first \verb|#include| section. See the example below. +All user-defined functions in the source code are instrumented with one or more underscore '\verb|_|' symbols appended to their names. The WMC tool counts the number of arguments in function calls and inserts a wrapper macro at the top of the source file, after the first \verb|#include| section. See the example below. \begin{Verbatim}[fontsize=\small] #include @@ -269,6 +274,49 @@ \subsection{User-defined functions} The WMC tool appends an additional underscore symbol '\verb|_|' in each variant of the user-defined function call. This means that if a function is defined to accept a variable number of arguments the first function call will have one underscore symbol '\verb|_|' appended to its name, the second function call will have two underscore symbols '\verb|__|' appended to its name and so on. The WMC tool supports up to 10 variants (different number of arguments) of the same function. +The WMC tool may encounter difficulties in properly instrumenting one-line functions that are defined as follows + +\begin{Verbatim}[fontsize=\small] + float calculate_vector_norm( float vec[], int_16t size ) { return sum2_f(vec, size); } +\end{Verbatim} + +It's advisable to define the function body on separate lines, like this + +\begin{Verbatim}[fontsize=\small] + float calculate_vector_norm( float vec[], int_16t size ) + { + return sum2_f(vec, size); + } +\end{Verbatim} + +In a local function, all declared variables should be positioned at the start of the function body. Additionally, the initialization of local variables should be segregated from their declaration. For instance, consider the following code snippet, where the WMC tool inserts \verb|$("M")| before \verb|int ch = 0;| + +\begin{Verbatim}[fontsize=\small] + int free_structs(MY_STRUCT_INFO *ptr_dec) + { + if (ptr_dec == NULL) return -1; + +$("M") int ch = 0; + + for (ch = 0; ch < n_channels; ch++) + { + struct_free(&decoder->cfg[ch]->fft_buf); + struct_free(&decoder->cfg[ch]->ifft_buf); + } + + return 0; + } +\end{Verbatim} + +Compiling the instrumented code provided above results in the following error (gcc): + +\begin{Verbatim}[fontsize=\small] +lib_thj/struct_f.c:373:17: error: expected expression before ‘int’ + 125 | $("M") int ch = 0; + | ^~~ +\end{Verbatim} + + %----------------------------------------------------------------------------- \subsection{Operators} @@ -364,7 +412,7 @@ \subsection{Operators} %----------------------------------------------------------------------------- \subsection{Manual instrumentation} -It was already shown in the previous section in this document that it is possible to avoid the automatic instrumentation by encapsulating parts of the source code with +As demonstrated in the preceding section of this document, it's possible to bypass automatic instrumentation by encapsulating sections of the source code with \begin{Verbatim}[fontsize=\small] #define WMC_TOOL_SKIP From eb0917dd0cb39b1e5dd3bc218c60af0fa1c21a19 Mon Sep 17 00:00:00 2001 From: malenov Date: Wed, 20 Mar 2024 13:35:47 +0100 Subject: [PATCH 50/53] fix error when des-instrumenting one-line functions --- src/wmc_tool/c_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wmc_tool/c_parser.cpp b/src/wmc_tool/c_parser.cpp index 0a4a5e2a..79f11010 100644 --- a/src/wmc_tool/c_parser.cpp +++ b/src/wmc_tool/c_parser.cpp @@ -7245,7 +7245,7 @@ TOOL_ERROR DesInstrument( { /* Yes */ /* Go To Start of Line */ tmp = ParseRec_ptr->item_start; - while ( --tmp, !IS_EOL_CHAR( *tmp ) ) + while ( --tmp, (IS_SPACE_CHAR( *tmp ) && !IS_EOL_CHAR( *tmp )) ) ; /* Advance (Past EOS) */ ptr += 2; From 4076d9b57d7f5371914e49c9e7d174e007a52446 Mon Sep 17 00:00:00 2001 From: Vladimir Malenovsky Date: Wed, 20 Mar 2024 17:03:13 +0100 Subject: [PATCH 51/53] fix the limitation of max string literals to 65535 with MSVC 2017 and older versions --- src/wmc_tool/wmc_auto_c.txt | 4344 +++++++++++++++++------------------ src/wmc_tool/wmc_auto_h.txt | 2896 +++++++++++------------ src/wmc_tool/wmc_tool.cpp | 65 +- 3 files changed, 3666 insertions(+), 3639 deletions(-) diff --git a/src/wmc_tool/wmc_auto_c.txt b/src/wmc_tool/wmc_auto_c.txt index 7d78d114..1b3b85e0 100644 --- a/src/wmc_tool/wmc_auto_c.txt +++ b/src/wmc_tool/wmc_auto_c.txt @@ -1,2172 +1,2172 @@ -"/*\r\n" -" * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved.\r\n" -" *\r\n" -" * This software is protected by copyright law and by international treaties. The source code, and all of its derivations,\r\n" -" * is provided by VoiceAge Corporation under the \"ITU-T Software Tools' General Public License\". Please, read the license file\r\n" -" * or refer to ITU-T Recommendation G.191 on \"SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS\".\r\n" -" *\r\n" -" * Any use of this software is permitted provided that this notice is not removed and that neither the authors nor\r\n" -" * VoiceAge Corporation are deemed to have made any representations as to the suitability of this software\r\n" -" * for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE.\r\n" -" *\r\n" -" * Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca)\r\n" -" */\r\n" -"\r\n" -"#include \r\n" -"#include \r\n" -"#include \r\n" -"#include \r\n" -"#include \r\n" -"\r\n" -"#ifndef _MSC_VER\r\n" -"#include \r\n" -"#include \r\n" -"#else\r\n" -"#include \r\n" -"#endif\r\n" -"\r\n" -"#include \"options.h\"\r\n" -"#include \"wmc_auto.h\"\r\n" -"\r\n" -"#define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */\r\n" -"\r\n" -"#ifdef WMOPS\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * Complexity counting tool\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"#define MAX_FUNCTION_NAME_LENGTH 50 /* Maximum length of the function name */\r\n" -"#define MAX_PARAMS_LENGTH 50 /* Maximum length of the function parameter string */\r\n" -"#define MAX_NUM_RECORDS 300 /* Initial maximum number of records -> mightb be increased during runtime, if needed */\r\n" -"#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of records, increase the number of records by this number */\r\n" -"#define MAX_CALL_TREE_DEPTH 100 /* maximum depth of the function call tree */\r\n" -"#define DOUBLE_MAX 0x80000000\r\n" -"#define FAC ( FRAMES_PER_SECOND / 1e6 )\r\n" -"\r\n" -"\r\n" -"typedef struct \r\n" -"{\r\n" -" char label[MAX_FUNCTION_NAME_LENGTH];\r\n" -" long call_number;\r\n" -" long update_cnt;\r\n" -" int call_tree[MAX_CALL_TREE_DEPTH];\r\n" -" long LastWOper;\r\n" -" double start_selfcnt;\r\n" -" double current_selfcnt;\r\n" -" double max_selfcnt;\r\n" -" double min_selfcnt;\r\n" -" double tot_selfcnt;\r\n" -" double start_cnt; \r\n" -" double current_cnt;\r\n" -" double max_cnt;\r\n" -" double min_cnt;\r\n" -" double tot_cnt;\r\n" -"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" int32_t current_call_number;\r\n" -" double wc_cnt;\r\n" -" double wc_selfcnt;\r\n" -" int32_t wc_call_number;\r\n" -"#endif\r\n" -"} wmops_record;\r\n" -"\r\n" -"double ops_cnt;\r\n" -"double prom_cnt;\r\n" -"double inst_cnt[NUM_INST];\r\n" -"\r\n" -"static wmops_record *wmops = NULL;\r\n" -"static int num_wmops_records, max_num_wmops_records;\r\n" -"static int current_record;\r\n" -"static long update_cnt;\r\n" -"static double start_cnt;\r\n" -"static double max_cnt;\r\n" -"static double min_cnt;\r\n" -"static double inst_cnt_wc[NUM_INST];\r\n" -"static long fnum_cnt_wc;\r\n" -"static int *wmops_caller_stack = NULL, wmops_caller_stack_index, max_wmops_caller_stack_index = 0;\r\n" -"static int *heap_allocation_call_tree = NULL, heap_allocation_call_tree_size = 0, heap_allocation_call_tree_max_size = 0;\r\n" -"\r\n" -"void reset_wmops( void )\r\n" -"{\r\n" -" int i, j;\r\n" -" unsigned int *ptr;\r\n" -"\r\n" -" num_wmops_records = 0;\r\n" -" max_num_wmops_records = MAX_NUM_RECORDS;\r\n" -" current_record = -1;\r\n" -" update_cnt = 0;\r\n" -"\r\n" -" max_cnt = 0.0;\r\n" -" min_cnt = DOUBLE_MAX;\r\n" -" start_cnt = 0.0;\r\n" -" ops_cnt = 0.0;\r\n" -"\r\n" -" /* allocate the list of wmops records */\r\n" -" if ( wmops == NULL )\r\n" -" {\r\n" -" wmops = (wmops_record *)malloc( max_num_wmops_records * sizeof( wmops_record ) );\r\n" -" }\r\n" -"\r\n" -" if ( wmops == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Error: Unable to Allocate List of WMOPS Records!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* allocate the BASOP WMOPS counter */\r\n" -" if ( multiCounter == NULL )\r\n" -" {\r\n" -" multiCounter = (BASIC_OP *) malloc( max_num_wmops_records * sizeof( BASIC_OP ) );\r\n" -" }\r\n" -"\r\n" -" if ( multiCounter == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Error: Unable to Allocate the BASOP WMOPS counter!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* initilize the list of wmops records */\r\n" -" /* initilize the BASOP WMOPS counters */\r\n" -" for ( i = 0; i < max_num_wmops_records; i++ )\r\n" -" {\r\n" -" strcpy( &wmops[i].label[0], \"\\0\" );\r\n" -" wmops[i].call_number = 0;\r\n" -" wmops[i].update_cnt = 0;\r\n" -" for ( j = 0; j < MAX_CALL_TREE_DEPTH; j++ )\r\n" -" {\r\n" -" wmops[i].call_tree[j] = -1;\r\n" -" }\r\n" -" wmops[i].start_selfcnt = 0.0;\r\n" -" wmops[i].current_selfcnt = 0.0;\r\n" -" wmops[i].max_selfcnt = 0.0;\r\n" -" wmops[i].min_selfcnt = DOUBLE_MAX;\r\n" -" wmops[i].tot_selfcnt = 0.0;\r\n" -" wmops[i].start_cnt = 0.0;\r\n" -" wmops[i].current_cnt = 0.0;\r\n" -" wmops[i].max_cnt = 0.0;\r\n" -" wmops[i].min_cnt = DOUBLE_MAX;\r\n" -" wmops[i].tot_cnt = 0.0;\r\n" -"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" wmops[i].wc_cnt = 0.0;\r\n" -" wmops[i].wc_selfcnt = 0.0;\r\n" -" wmops[i].current_call_number = 0;\r\n" -" wmops[i].wc_call_number = -1;\r\n" -"#endif\r\n" -"\r\n" -" /* clear all BASOP operation counters */\r\n" -" ptr = (unsigned int*) &multiCounter[i];\r\n" -" for ( j = 0; j < (int) ( sizeof(BASIC_OP ) / sizeof( unsigned int ) ); j++ )\r\n" -" {\r\n" -" *ptr++ = 0;\r\n" -" }\r\n" -" wmops[i].LastWOper = 0;\r\n" -" }\r\n" -"\r\n" -" /* allocate the list of wmops callers to track the sequence of function calls */\r\n" -" wmops_caller_stack_index = 0;\r\n" -" max_wmops_caller_stack_index = MAX_NUM_RECORDS;\r\n" -" if ( wmops_caller_stack == NULL )\r\n" -" {\r\n" -" wmops_caller_stack = malloc( max_wmops_caller_stack_index * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" if ( wmops_caller_stack == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Error: Unable to Allocate List of WMOPS Callers!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" for ( i = 0; i < max_wmops_caller_stack_index; i++ )\r\n" -" {\r\n" -" wmops_caller_stack[i] = -1;\r\n" -" }\r\n" -"\r\n" -" /* initialize auxiliary BASOP WMOPS variables */\r\n" -" call_occurred = 1;\r\n" -" funcId_where_last_call_to_else_occurred = INT_MAX;\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"\r\n" -"void push_wmops( const char *label )\r\n" -"{\r\n" -" int new_flag;\r\n" -" int i, j;\r\n" -"\r\n" -" /* Check, if this is a new function label */\r\n" -" new_flag = 1;\r\n" -" for ( i = 0; i < num_wmops_records; i++ )\r\n" -" {\r\n" -" if ( strcmp( wmops[i].label, label ) == 0 )\r\n" -" {\r\n" -" new_flag = 0;\r\n" -" break;\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Create a new record in the list */\r\n" -" if ( new_flag )\r\n" -" {\r\n" -" if ( num_wmops_records >= max_num_wmops_records )\r\n" -" {\r\n" -" /* There is no room for a new wmops record -> reallocate the list */\r\n" -" max_num_wmops_records += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" wmops = realloc( wmops, max_num_wmops_records * sizeof( wmops_record ) );\r\n" -" multiCounter = realloc( multiCounter, max_num_wmops_records * sizeof( BASIC_OP ) );\r\n" -" }\r\n" -"\r\n" -" strcpy( wmops[i].label, label );\r\n" -"\r\n" -" num_wmops_records++;\r\n" -" }\r\n" -"\r\n" -" /* Push the current context info to the new record */\r\n" -" if ( current_record >= 0 )\r\n" -" {\r\n" -" if ( wmops_caller_stack_index >= max_wmops_caller_stack_index )\r\n" -" {\r\n" -" /* There is no room for a new record -> reallocate the list */\r\n" -" max_wmops_caller_stack_index += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" wmops_caller_stack = realloc( wmops_caller_stack, max_wmops_caller_stack_index * sizeof( int ) );\r\n" -" }\r\n" -" wmops_caller_stack[wmops_caller_stack_index++] = current_record;\r\n" -"\r\n" -" /* accumulate op counts */\r\n" -" wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt;\r\n" -"\r\n" -" /* update call tree */\r\n" -" for ( j = 0; j < MAX_CALL_TREE_DEPTH; j++ )\r\n" -" {\r\n" -" if ( wmops[i].call_tree[j] == current_record )\r\n" -" {\r\n" -" break;\r\n" -" }\r\n" -" else if ( wmops[i].call_tree[j] == -1 )\r\n" -" {\r\n" -" wmops[i].call_tree[j] = current_record;\r\n" -" break;\r\n" -" }\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* update the current context info */\r\n" -" current_record = i;\r\n" -" wmops[current_record].start_selfcnt = ops_cnt;\r\n" -" wmops[current_record].start_cnt = ops_cnt;\r\n" -" wmops[current_record].call_number++;\r\n" -"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" wmops[current_record].current_call_number++;\r\n" -"#endif\r\n" -"\r\n" -" /* set the ID of BASOP functions counters */\r\n" -" Set_BASOP_WMOPS_counter( current_record );\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"\r\n" -"void pop_wmops( void )\r\n" -"{\r\n" -" long tot;\r\n" -"\r\n" -" /* Check for underflow */\r\n" -" if ( current_record < 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"\\r pop_wmops(): stack underflow, too many calls to pop_wmops()\\n\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* add the BASOP complexity to the counter */\r\n" -" tot = DeltaWeightedOperation();\r\n" -" ops_cnt += tot;\r\n" -"\r\n" -" /* update count of current record */\r\n" -" wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt;\r\n" -" wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt;\r\n" -"\r\n" -" /* Get back previous context from stack */\r\n" -" if ( wmops_caller_stack_index > 0 )\r\n" -" {\r\n" -" current_record = wmops_caller_stack[--wmops_caller_stack_index];\r\n" -" wmops[current_record].start_selfcnt = ops_cnt;\r\n" -"\r\n" -" /* set the ID of the previous BASOP counter */\r\n" -" Set_BASOP_WMOPS_counter( current_record );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" current_record = -1;\r\n" -" }\r\n" -"\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"\r\n" -"void update_wmops( void )\r\n" -"{\r\n" -" int i;\r\n" -" double current_cnt;\r\n" -"#ifdef WMOPS_PER_FRAME\r\n" -" static FILE *fid = NULL;\r\n" -" const char filename[] = \"wmops_analysis\";\r\n" -" float tmpF;\r\n" -"#endif\r\n" -"\r\n" -" if ( wmops_caller_stack_index != 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"update_wmops(): WMOPS caller stack corrupted - check that all push_wmops() are matched with pop_wmops()!\\n\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -"#ifdef WMOPS_PER_FRAME\r\n" -" /* Check, if the output file has already been opened */\r\n" -" if ( fid == NULL )\r\n" -" {\r\n" -" fid = fopen( filename, \"wb\" );\r\n" -"\r\n" -" if ( fid == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"\\nCannot open %s!\\n\\n\", filename );\r\n" -" exit( -1 );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Write current complexity to the external file */\r\n" -" tmpF = (float) ( FAC * wmops[0].current_cnt );\r\n" -" fwrite( &tmpF, sizeof( float ), 1, fid );\r\n" -"#endif\r\n" -"\r\n" -"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" if ( ops_cnt - start_cnt > max_cnt )\r\n" -" {\r\n" -" for ( i = 0; i < num_wmops_records; i++ )\r\n" -" {\r\n" -" wmops[i].wc_cnt = wmops[i].current_cnt;\r\n" -" wmops[i].wc_selfcnt = wmops[i].current_selfcnt;\r\n" -" wmops[i].wc_call_number = wmops[i].current_call_number;\r\n" -" }\r\n" -" }\r\n" -"#endif\r\n" -"\r\n" -" for ( i = 0; i < num_wmops_records; i++ )\r\n" -" {\r\n" -" wmops[i].tot_selfcnt += wmops[i].current_selfcnt;\r\n" -" wmops[i].tot_cnt += wmops[i].current_cnt;\r\n" -"\r\n" -" if ( wmops[i].current_selfcnt > 0 )\r\n" -" {\r\n" -" if ( wmops[i].current_selfcnt > wmops[i].max_selfcnt )\r\n" -" {\r\n" -" wmops[i].max_selfcnt = wmops[i].current_selfcnt;\r\n" -" }\r\n" -"\r\n" -" if ( wmops[i].current_selfcnt < wmops[i].min_selfcnt )\r\n" -" {\r\n" -" wmops[i].min_selfcnt = wmops[i].current_selfcnt;\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" wmops[i].current_selfcnt = 0;\r\n" -"\r\n" -" if ( wmops[i].current_cnt > 0 )\r\n" -" {\r\n" -" if ( wmops[i].current_cnt > wmops[i].max_cnt )\r\n" -" {\r\n" -" wmops[i].max_cnt = wmops[i].current_cnt;\r\n" -" }\r\n" -"\r\n" -"\r\n" -" if ( wmops[i].current_cnt < wmops[i].min_cnt )\r\n" -" {\r\n" -" wmops[i].min_cnt = wmops[i].current_cnt;\r\n" -" }\r\n" -"\r\n" -" wmops[i].update_cnt++;\r\n" -" }\r\n" -"\r\n" -" wmops[i].current_cnt = 0;\r\n" -"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" wmops[i].current_call_number = 0;\r\n" -"#endif\r\n" -"\r\n" -" /* update the WC of all BASOP counters */\r\n" -" Set_BASOP_WMOPS_counter( i );\r\n" -" Reset_BASOP_WMOPS_counter();\r\n" -" }\r\n" -"\r\n" -" current_cnt = ops_cnt - start_cnt;\r\n" -" if ( current_cnt > max_cnt )\r\n" -" {\r\n" -" max_cnt = current_cnt;\r\n" -"\r\n" -" for ( i = 0; i < NUM_INST; i++ )\r\n" -" {\r\n" -" inst_cnt_wc[i] = inst_cnt[i];\r\n" -" }\r\n" -"\r\n" -" fnum_cnt_wc = update_cnt + 1;\r\n" -" }\r\n" -"\r\n" -" if ( current_cnt < min_cnt )\r\n" -" {\r\n" -" min_cnt = current_cnt;\r\n" -" }\r\n" -"\r\n" -" for ( i = 0; i < NUM_INST; i++ )\r\n" -" {\r\n" -" inst_cnt[i] = 0.0;\r\n" -" }\r\n" -"\r\n" -" start_cnt = ops_cnt;\r\n" -"\r\n" -" /* increment frame counter */\r\n" -" update_cnt++;\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"\r\n" -"void print_wmops( void )\r\n" -"{\r\n" -" int i, label_len, max_label_len;\r\n" -"\r\n" -" char *sfmts = \"%*s %8s %8s %7s %7s\\n\";\r\n" -" char *dfmts = \"%*s %8.2f %8.3f %7.3f %7.3f\\n\";\r\n" -" char *sfmt = \"%*s %8s %8s %7s %7s %7s %7s %7s\\n\";\r\n" -" char *dfmt = \"%*s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\\n\";\r\n" -"\r\n" -"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" int j;\r\n" -" char *sfmtt = \"%20s %4s %15s\\n\";\r\n" -" char *dfmtt = \"%20s %4d \";\r\n" -"#endif\r\n" -"\r\n" -" /* calculate maximum label length for compact prinout */\r\n" -" max_label_len = 0;\r\n" -" for ( i = 0; i < num_wmops_records; i++ )\r\n" -" {\r\n" -" label_len = strlen( wmops[i].label );\r\n" -" if ( label_len > max_label_len )\r\n" -" {\r\n" -" max_label_len = label_len;\r\n" -" }\r\n" -" }\r\n" -" max_label_len += 4;\r\n" -"\r\n" -" fprintf( stdout, \"\\n\\n --- Complexity analysis [WMOPS] --- \\n\\n\" );\r\n" -" \r\n" -" fprintf( stdout, \"%*s %33s %23s\\n\", max_label_len, \"\", \"|------ SELF ------|\", \"|--- CUMULATIVE ---|\" );\r\n" -" fprintf( stdout, sfmt, max_label_len, \" routine\", \" calls\", \" min \", \" max \", \" avg \", \" min \", \" max \", \" avg \" );\r\n" -" fprintf( stdout, sfmt, max_label_len, \"---------------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\" );\r\n" -"\r\n" -" for ( i = 0; i < num_wmops_records; i++ )\r\n" -" {\r\n" -" fprintf( stdout, dfmt, max_label_len, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt,\r\n" -" wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt,\r\n" -" FAC * wmops[i].max_selfcnt,\r\n" -" wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_selfcnt / wmops[i].update_cnt,\r\n" -" wmops[i].min_cnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_cnt,\r\n" -" FAC * wmops[i].max_cnt,\r\n" -" wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_cnt / wmops[i].update_cnt );\r\n" -" }\r\n" -"\r\n" -" fprintf( stdout, sfmts, max_label_len, \"---------------\", \"------\", \"------\", \"------\", \"------\" );\r\n" -" fprintf( stdout, dfmts, max_label_len, \"total\", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt );\r\n" -" fprintf( stdout, \"\\n\" );\r\n" -"\r\n" -"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n" -" fprintf( stdout, \"\\nComplexity analysis for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc );\r\n" -" fprintf( stdout, \"%*s %8s %10s %12s\\n\", max_label_len, \" routine\", \" calls\", \" SELF\", \" CUMULATIVE\" );\r\n" -" fprintf( stdout, \"%*s %8s %10s %10s\\n\", max_label_len, \"---------------\", \"------\", \"------\", \"----------\" );\r\n" -"\r\n" -" for ( i = 0; i < num_wmops_records; i++ )\r\n" -" {\r\n" -" if ( wmops[i].wc_call_number > 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"%*s %8d %10.3f %12.3f\\n\", max_label_len, wmops[i].label, wmops[i].wc_call_number, FAC * wmops[i].wc_selfcnt, FAC * wmops[i].wc_cnt );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" fprintf( stdout, \"\\nCall tree for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc );\r\n" -" fprintf( stdout, sfmtt, \" function\", \"num\", \"called by \" );\r\n" -" fprintf( stdout, sfmtt, \"---------------\", \"---\", \"--------------\" );\r\n" -"\r\n" -" for ( i = 0; i < num_wmops_records; i++ )\r\n" -" {\r\n" -" if ( wmops[i].wc_call_number > 0 )\r\n" -" {\r\n" -" fprintf( stdout, dfmtt, wmops[i].label, i );\r\n" -" for ( j = 0; wmops[i].call_tree[j] != -1 && j < MAX_CALL_TREE_DEPTH; j++ )\r\n" -" {\r\n" -" if ( j != 0 )\r\n" -" {\r\n" -" fprintf( stdout, \", \" );\r\n" -" }\r\n" -" fprintf( stdout, \"%d\", wmops[i].call_tree[j] );\r\n" -" }\r\n" -" fprintf( stdout, \"\\n\" );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" fprintf( stdout, \"\\n\\n\" );\r\n" -"\r\n" -" fprintf( stdout, \"\\nInstruction type analysis for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc ); \r\n" -" for ( i = 0; i < NUM_INST; i++ )\r\n" -" {\r\n" -" switch ( (enum instructions) i )\r\n" -" {\r\n" -" case _ADD:\r\n" -" fprintf( stdout, \"\\tAdds: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _ABS:\r\n" -" fprintf( stdout, \"\\tAbsolutes: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _MULT:\r\n" -" fprintf( stdout, \"\\tMultiplies: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _MAC:\r\n" -" fprintf( stdout, \"\\tMACs: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _MOVE:\r\n" -" fprintf( stdout, \"\\tMoves: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _STORE:\r\n" -" fprintf( stdout, \"\\tStores: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _LOGIC:\r\n" -" fprintf( stdout, \"\\tLogicals: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _SHIFT:\r\n" -" fprintf( stdout, \"\\tShifts: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _BRANCH:\r\n" -" fprintf( stdout, \"\\tBranches: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _DIV:\r\n" -" fprintf( stdout, \"\\tDivisions: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _SQRT:\r\n" -" fprintf( stdout, \"\\tSquare Root: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _TRANS:\r\n" -" fprintf( stdout, \"\\tTrans: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _FUNC:\r\n" -" fprintf( stdout, \"\\tFunc Call: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _LOOP:\r\n" -" fprintf( stdout, \"\\tLoop Init: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _INDIRECT:\r\n" -" fprintf( stdout, \"\\tIndirect Addr: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _PTR_INIT:\r\n" -" fprintf( stdout, \"\\tPointer Init: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _TEST:\r\n" -" fprintf( stdout, \"\\tExtra condit.: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _POWER:\r\n" -" fprintf( stdout, \"\\tExponential: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _LOG:\r\n" -" fprintf( stdout, \"\\tLogarithm: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" case _MISC:\r\n" -" fprintf( stdout, \"\\tAll other op.: %12.1f\\n\", inst_cnt_wc[i] );\r\n" -" break;\r\n" -" default:\r\n" -" fprintf( stdout, \"\\tERROR: Invalid instruction type: %d\\n\\n\", i );\r\n" -" }\r\n" -" }\r\n" -"#endif\r\n" -"\r\n" -" /* De-allocate the list of wmops record */\r\n" -" if ( wmops != NULL )\r\n" -" {\r\n" -" free( wmops );\r\n" -" }\r\n" -"\r\n" -" /* De-allocate the list of wmops caller functions */\r\n" -" if ( wmops_caller_stack != NULL )\r\n" -" {\r\n" -" free( wmops_caller_stack );\r\n" -" }\r\n" -"\r\n" -" /* De-allocate the BASOP WMOPS counter */\r\n" -" if ( multiCounter != NULL )\r\n" -" {\r\n" -" free( multiCounter );\r\n" -" }\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * Memory counting tool measuring RAM usage (stack and heap)\r\n" -" *\r\n" -" * Maximum RAM is measured by monitoring the total allocated memory (stack and heap) in each frame.\r\n" -" *\r\n" -" * Maximum stack is measured by monitoring the difference between the 'top' and 'bottom' of the stack. The 'bottom' of the stack is updated in each function\r\n" -" * with a macro 'func_start_' which is inserted automatically to all functions during the instrumentation process.\r\n" -" *\r\n" -" * Maximum heap is measured by summing the sizes of all memory blocks allocated by malloc() or calloc() and deallocated by free(). The maximum heap size is\r\n" -" * updated each time when the macros malloc_() or calloc_() is invoked. The macros 'malloc_ and calloc_' are inserted automatically during the instrumentation process.\r\n" -" * As part of heap measurements, intra-frame heap and inter-frame heap are measured separately. Intra-frame heap refers to heap memory which is allocated and deallocated\r\n" -" * within a single frame. Inter-frame heap, on the contrary, refers to heap memory which is reserved for more than one frame.\r\n" -" *\r\n" -" * In order to run the memory counting tool the function reset_mem(cnt_size) must be called at the beginning of the encoding/decoding process.\r\n" -" * The unit in which memory consumption is reported is set via the parameter 'cnt_size'. It can be set to 0 (bytes), 1 (32b words) or 2 (64b words).\r\n" -" * At the end of the encoding/decoding process, 'print_mem()' function may be called to print basic information about memory consumption. If the macro 'MEM_COUNT_DETAILS'\r\n" -" * is activated, detailed information is printed\r\n" -" *\r\n" -" * The macro 'WMOPS' needs to be activated to enable memory counting. To avoid the instrumentation of malloc()/calloc()/free() calls, use\r\n" -" * #define WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP macro pair around the malloc(), calloc() and free().\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"/* This is the value (in bytes) towards which the block size is rounded. For example, a block of 123 bytes, when using\r\n" -" a 32 bits system, will end up taking 124 bytes since the last unused byte cannot be used for another block. */\r\n" -"#ifdef MEM_ALIGN_64BITS\r\n" -"#define BLOCK_ROUNDING 8 /* Align on 64 Bits */\r\n" -"#else\r\n" -"#define BLOCK_ROUNDING 4 /* Align on 32 Bits */\r\n" -"#endif\r\n" -"\r\n" -"#define N_32BITS_BLOCKS ( BLOCK_ROUNDING / sizeof( int32_t ) )\r\n" -"#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) )\r\n" -"\r\n" -"#define MAGIC_VALUE_OOB 0x12A534F0 /* Signature value which is inserted before and after each allocated memory block, used to detect out-of-bound access */\r\n" -"#define MAGIC_VALUE_USED ( ~MAGIC_VALUE_OOB ) /* Value used to pre-fill allocated memory blocks, used to calculate actual memory usage */\r\n" -"#define OOB_START 0x1 /* Flag indicating out-of-bounds access before memory block */\r\n" -"#define OOB_END 0x2 /* Flag indicating out-of-bounds access after memory block */\r\n" -"\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -"const char *csv_filename = \"mem_analysis.csv\";\r\n" -"static FILE *fid_csv_filename = NULL;\r\n" -"#endif\r\n" -"\r\n" -"typedef struct\r\n" -"{\r\n" -" char function_name[MAX_FUNCTION_NAME_LENGTH + 1];\r\n" -" int16_t *stack_ptr;\r\n" -"} caller_info;\r\n" -"\r\n" -"static caller_info *stack_callers[2] = {NULL, NULL};\r\n" -"\r\n" -"static int16_t *ptr_base_stack = 0; /* Pointer to the bottom of stack (base pointer). Stack grows up. */\r\n" -"static int16_t *ptr_current_stack = 0; /* Pointer to the current stack pointer */\r\n" -"static int16_t *ptr_max_stack = 0; /* Pointer to the maximum stack pointer (the farest point from the bottom of stack) */\r\n" -"static int32_t wc_stack_frame = 0; /* Frame corresponding to the worst-case stack usage */\r\n" -"static int current_calls = 0, max_num_calls = MAX_NUM_RECORDS;\r\n" -"static char location_max_stack[256] = \"undefined\";\r\n" -"\r\n" -"/* Heap-related variables */\r\n" -"typedef struct\r\n" -"{\r\n" -" char name[MAX_FUNCTION_NAME_LENGTH + 1]; /* +1 for NUL */\r\n" -" char params[1 + MAX_PARAMS_LENGTH + 1]; /* +1 for 'm'/'c' alloc & +1 for NUL */\r\n" -" unsigned long hash;\r\n" -" int lineno;\r\n" -" void *block_ptr;\r\n" -" int block_size;\r\n" -" unsigned long total_block_size; /* Cumulative sum of the allocated size in the session */\r\n" -" unsigned long total_used_size; /* Cumulative sum of the used size in the session */\r\n" -" int wc_heap_size_intra_frame; /* Worst-Case Intra-Frame Heap Size */\r\n" -" int wc_heap_size_inter_frame; /* Worst-Case Inter-Frame Heap Size */\r\n" -" int frame_allocated; /* Frame number in which the Memory Block has been allocated (-1 if not allocated at the moment) */\r\n" -" int OOB_Flag;\r\n" -" int noccurances; /* Number of times that the memory block has been allocated in a frame */\r\n" -"} allocator_record;\r\n" -"\r\n" -"allocator_record *allocation_list = NULL;\r\n" -"\r\n" -"static int Num_Records, Max_Num_Records;\r\n" -"static size_t Stat_Cnt_Size = USE_BYTES;\r\n" -"static const char *Count_Unit[] = { \"bytes\", \"words\", \"words\", \"words\" };\r\n" -"\r\n" -"static int32_t wc_ram_size, wc_ram_frame;\r\n" -"static int32_t current_heap_size;\r\n" -"static int *list_wc_intra_frame_heap, n_items_wc_intra_frame_heap, max_items_wc_intra_frame_heap, size_wc_intra_frame_heap, location_wc_intra_frame_heap;\r\n" -"static int *list_current_inter_frame_heap, n_items_current_inter_frame_heap, max_items_current_inter_frame_heap, size_current_inter_frame_heap;\r\n" -"static int *list_wc_inter_frame_heap, n_items_wc_inter_frame_heap, max_items_wc_inter_frame_heap, size_wc_inter_frame_heap, location_wc_inter_frame_heap;\r\n" -"\r\n" -"/* Local Functions */\r\n" -"static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str );\r\n" -"allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record );\r\n" -"static void *mem_alloc_block( size_t size, const char *size_str );\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * reset_mem()\r\n" -" *\r\n" -" * Initialize/reset memory counting tool (stack and heap)\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"void reset_mem( Counting_Size cnt_size )\r\n" -"{\r\n" -" int16_t something;\r\n" -" size_t tmp_size;\r\n" -"\r\n" -" /* initialize list of stack records */\r\n" -" if ( stack_callers[0] == NULL )\r\n" -" {\r\n" -" stack_callers[0] = malloc( MAX_NUM_RECORDS * sizeof( caller_info ) );\r\n" -" stack_callers[1] = malloc( MAX_NUM_RECORDS * sizeof( caller_info ) );\r\n" -" }\r\n" -"\r\n" -" if ( stack_callers[0] == NULL || stack_callers[1] == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Error: Unable to Allocate List of Stack Records!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" current_calls = 0;\r\n" -" max_num_calls = MAX_NUM_RECORDS;\r\n" -"\r\n" -" /* initialize stack pointers */\r\n" -" ptr_base_stack = &something;\r\n" -" ptr_max_stack = ptr_base_stack;\r\n" -" ptr_current_stack = ptr_base_stack;\r\n" -"\r\n" -" /* initialize the unit of memory block size */\r\n" -" Stat_Cnt_Size = cnt_size;\r\n" -"\r\n" -" /* Check, if sizeof(int32_t) is 4 bytes */\r\n" -" tmp_size = sizeof( int32_t );\r\n" -" if ( tmp_size != 4 )\r\n" -" {\r\n" -" fprintf( stderr, \"Error: Expecting 'int32_t' to be a 32 Bits Integer!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* create allocation list for malloc() memory blocks */\r\n" -" if ( allocation_list == NULL )\r\n" -" {\r\n" -" allocation_list = malloc( MAX_NUM_RECORDS * sizeof( allocator_record ) );\r\n" -" }\r\n" -"\r\n" -" if ( allocation_list == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Error: Unable to Create List of Memory Blocks!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" Num_Records = 0;\r\n" -" Max_Num_Records = MAX_NUM_RECORDS;\r\n" -"\r\n" -" wc_ram_size = 0;\r\n" -" wc_ram_frame = -1;\r\n" -" current_heap_size = 0;\r\n" -"\r\n" -" /* heap allocation tree */\r\n" -" heap_allocation_call_tree_max_size = MAX_NUM_RECORDS;\r\n" -" if ( heap_allocation_call_tree == NULL )\r\n" -" {\r\n" -" heap_allocation_call_tree = (int *) malloc( heap_allocation_call_tree_max_size * sizeof( int ) );\r\n" -" memset( heap_allocation_call_tree, -1, heap_allocation_call_tree_max_size * sizeof( int ) );\r\n" -" }\r\n" -" heap_allocation_call_tree_size = 0;\r\n" -"\r\n" -" /* wc intra-frame heap */\r\n" -" max_items_wc_intra_frame_heap = MAX_NUM_RECORDS;\r\n" -" if ( list_wc_intra_frame_heap == NULL )\r\n" -" {\r\n" -" list_wc_intra_frame_heap = (int *) malloc( max_items_wc_intra_frame_heap * sizeof( int ) );\r\n" -" memset( list_wc_intra_frame_heap, -1, max_items_wc_intra_frame_heap * sizeof( int ) );\r\n" -" }\r\n" -" n_items_wc_intra_frame_heap = 0;\r\n" -" size_wc_intra_frame_heap = 0;\r\n" -" location_wc_intra_frame_heap = -1;\r\n" -"\r\n" -" /* current inter-frame heap */\r\n" -" max_items_current_inter_frame_heap = MAX_NUM_RECORDS;\r\n" -" if ( list_current_inter_frame_heap == NULL )\r\n" -" {\r\n" -" list_current_inter_frame_heap = (int *) malloc( max_items_current_inter_frame_heap * sizeof( int ) );\r\n" -" memset( list_current_inter_frame_heap, -1, max_items_current_inter_frame_heap * sizeof( int ) );\r\n" -" }\r\n" -" n_items_current_inter_frame_heap = 0;\r\n" -" size_current_inter_frame_heap = 0;\r\n" -"\r\n" -" /* wc inter-frame heap */\r\n" -" max_items_wc_inter_frame_heap = MAX_NUM_RECORDS;\r\n" -" if ( list_wc_inter_frame_heap == NULL )\r\n" -" {\r\n" -" list_wc_inter_frame_heap = (int *) malloc( max_items_wc_inter_frame_heap * sizeof( int ) );\r\n" -" memset( list_wc_inter_frame_heap, -1, max_items_wc_inter_frame_heap * sizeof( int ) );\r\n" -" }\r\n" -" n_items_wc_inter_frame_heap = 0;\r\n" -" size_wc_inter_frame_heap = 0;\r\n" -" location_wc_inter_frame_heap = -1;\r\n" -"\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -" /* Check, if the .csv file has already been opened */\r\n" -" if ( fid_csv_filename == NULL )\r\n" -" {\r\n" -" fid_csv_filename = fopen( csv_filename, \"wb\" );\r\n" -"\r\n" -" if ( fid_csv_filename == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"\\nCannot open %s!\\n\\n\", csv_filename );\r\n" -" exit( -1 );\r\n" -" }\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" /* reset file */\r\n" -" rewind( fid_csv_filename );\r\n" -" }\r\n" -"#endif\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * reset_stack()\r\n" -" *\r\n" -" * Reset stack pointer\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"void reset_stack( void )\r\n" -"{\r\n" -" int16_t something;\r\n" -"\r\n" -" /* initialize/reset stack pointers */\r\n" -" ptr_base_stack = &something;\r\n" -" ptr_max_stack = ptr_base_stack;\r\n" -" ptr_current_stack = ptr_base_stack;\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * push_stack()\r\n" -" *\r\n" -" * Check the current stack pointer and update the maximum stack pointer, if new maximum found.\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"int push_stack( const char *filename, const char *fctname )\r\n" -"{\r\n" -" int16_t something;\r\n" -" int32_t current_stack_size;\r\n" -"\r\n" -" ptr_current_stack = &something;\r\n" -"\r\n" -" (void) *filename; /* to avoid compilation warning */\r\n" -"\r\n" -" if ( current_calls >= max_num_calls )\r\n" -" {\r\n" -" /* There is no room for a new record -> reallocate the list */\r\n" -" max_num_calls += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" stack_callers[0] = realloc( stack_callers[0], max_num_calls * sizeof( caller_info ) );\r\n" -" stack_callers[1] = realloc( stack_callers[1], max_num_calls * sizeof( caller_info ) );\r\n" -" }\r\n" -"\r\n" -" /* Valid Function Name? */\r\n" -" if ( fctname[0] == 0 )\r\n" -" { /* No */\r\n" -" fprintf( stderr, \"Invalid function name for call stack info.\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* Save the Name of the Calling Function in the Table */\r\n" -" strncpy( stack_callers[0][current_calls].function_name, fctname, MAX_FUNCTION_NAME_LENGTH );\r\n" -" stack_callers[0][current_calls].function_name[MAX_FUNCTION_NAME_LENGTH] = 0; /* Nul Terminate */\r\n" -"\r\n" -" /* Save the Stack Pointer */\r\n" -" stack_callers[0][current_calls].stack_ptr = ptr_current_stack;\r\n" -"\r\n" -" /* Increase the Number of Calls in the List */\r\n" -" current_calls++;\r\n" -"\r\n" -" /* Is this the First Time or the Worst Case? */\r\n" -" if ( ptr_current_stack < ptr_max_stack || ptr_max_stack == NULL )\r\n" -" { /* Yes */\r\n" -" /* Save Info about it */\r\n" -" ptr_max_stack = ptr_current_stack;\r\n" -"\r\n" -" /* save the worst-case frame number */\r\n" -" /* current frame number is stored in the variable update_cnt and updated in the function update_wmops() */\r\n" -" wc_stack_frame = update_cnt; \r\n" -" strncpy( location_max_stack, fctname, sizeof( location_max_stack ) - 1 );\r\n" -" location_max_stack[sizeof( location_max_stack ) - 1] = '\\0';\r\n" -"\r\n" -" /* Save Call Tree */\r\n" -" memmove( stack_callers[1], stack_callers[0], sizeof( caller_info ) * current_calls );\r\n" -"\r\n" -" /* Terminate the List with 0 (for printing purposes) */\r\n" -" if ( current_calls < max_num_calls )\r\n" -" {\r\n" -" stack_callers[1][current_calls].function_name[0] = 0;\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Check, if This is the New Worst-Case RAM (stack + heap) */\r\n" -" current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) );\r\n" -"\r\n" -" if ( current_stack_size < 0 )\r\n" -" {\r\n" -" /* prevent negative stack size */\r\n" -" current_stack_size = 0;\r\n" -" }\r\n" -"\r\n" -" if ( current_stack_size + current_heap_size > wc_ram_size )\r\n" -" {\r\n" -" wc_ram_size = current_stack_size + current_heap_size;\r\n" -" wc_ram_frame = update_cnt;\r\n" -" }\r\n" -"\r\n" -" return 0 /* for Now */;\r\n" -"}\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * pop_stack()\r\n" -" *\r\n" -" * Remove stack caller entry from the list\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"int pop_stack( const char *filename, const char *fctname )\r\n" -"{\r\n" -" caller_info *caller_info_ptr;\r\n" -"\r\n" -" (void) *filename; /* to avoid compilation warning */\r\n" -"\r\n" -" /* Decrease the Number of Records */\r\n" -" current_calls--;\r\n" -"\r\n" -" /* Get Pointer to Caller Information */\r\n" -" caller_info_ptr = &stack_callers[0][current_calls];\r\n" -"\r\n" -" /* Check, if the Function Names Match */\r\n" -" if ( strncmp( caller_info_ptr->function_name, fctname, MAX_FUNCTION_NAME_LENGTH ) != 0 )\r\n" -" {\r\n" -" fprintf( stderr, \"Invalid usage of pop_stack()\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* Erase Entry */\r\n" -" caller_info_ptr->function_name[0] = 0;\r\n" -"\r\n" -" /* Retrieve previous stack pointer */\r\n" -" if ( current_calls == 0 )\r\n" -" {\r\n" -" ptr_current_stack = ptr_base_stack;\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" ptr_current_stack = stack_callers[0][current_calls - 1].stack_ptr;\r\n" -" }\r\n" -"\r\n" -" return 0 /* for Now */;\r\n" -"}\r\n" -"\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * print_stack_call_tree()\r\n" -" *\r\n" -" * Print detailed information about worst-case stack usage\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"static void print_stack_call_tree( void )\r\n" -"{\r\n" -" caller_info *caller_info_ptr;\r\n" -" int call_level;\r\n" -" char fctname[MAX_FUNCTION_NAME_LENGTH + 1];\r\n" -"\r\n" -" fprintf( stdout, \"\\nList of functions when maximum stack size is reached:\\n\\n\" );\r\n" -"\r\n" -" caller_info_ptr = &stack_callers[1][0];\r\n" -" for ( call_level = 0; call_level < max_num_calls; call_level++ )\r\n" -" {\r\n" -" /* Done? */\r\n" -" if ( caller_info_ptr->function_name[0] == 0 )\r\n" -" {\r\n" -" break;\r\n" -" }\r\n" -"\r\n" -" /* Print Name */\r\n" -" strncpy( fctname, caller_info_ptr->function_name, MAX_FUNCTION_NAME_LENGTH );\r\n" -" strcat( fctname, \"()\" );\r\n" -" fprintf( stdout, \"%-42s\", fctname );\r\n" -"\r\n" -" /* Print Stack Usage (Based on Difference) */\r\n" -" if ( call_level != 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"%lu %s\\n\", ( ( ( caller_info_ptr - 1 )->stack_ptr - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" fprintf( stdout, \"%lu %s\\n\", ( ( ptr_base_stack - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] );\r\n" -" }\r\n" -"\r\n" -" /* Advance */\r\n" -" caller_info_ptr++;\r\n" -" }\r\n" -"\r\n" -" fprintf( stdout, \"\\n\" );\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"#endif\r\n" -"\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * mem_alloc()\r\n" -" *\r\n" -" * Creates new record, stores auxiliary information about which function allocated the memory, line number, parameters, etc.\r\n" -" * Finally, it allocates physical memory using malloc()\r\n" -" * The function also updates worst-case heap size and worst-case RAM size\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"void *mem_alloc(\r\n" -" const char *func_name,\r\n" -" int func_lineno,\r\n" -" size_t size,\r\n" -" char *size_str /* the first char indicates m-alloc or c-alloc */ )\r\n" -"{\r\n" -" int index_record;\r\n" -" int32_t current_stack_size;\r\n" -" unsigned long hash;\r\n" -" allocator_record *ptr_record;\r\n" -"\r\n" -" if ( size == 0 )\r\n" -" {\r\n" -" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Size of Zero not Supported\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* Search for an existing record (that has been de-allocated before) */\r\n" -" index_record = 0;\r\n" -" while ( ( ptr_record = get_mem_record( &hash, func_name, func_lineno, size_str, &index_record ) ) != NULL )\r\n" -" {\r\n" -" if ( ptr_record->frame_allocated == -1 )\r\n" -" {\r\n" -" break;\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" index_record++;\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Create new record */\r\n" -" if ( ptr_record == NULL )\r\n" -" {\r\n" -" if ( Num_Records >= Max_Num_Records )\r\n" -" {\r\n" -" /* There is no room for a new record -> reallocate memory */\r\n" -" Max_Num_Records += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" allocation_list = realloc( allocation_list, Max_Num_Records * sizeof( allocator_record ) );\r\n" -" }\r\n" -"\r\n" -" ptr_record = &( allocation_list[Num_Records] );\r\n" -"\r\n" -" /* Initialize new record */\r\n" -" ptr_record->hash = hash;\r\n" -" ptr_record->noccurances = 0;\r\n" -" ptr_record->total_block_size = 0;\r\n" -" ptr_record->total_used_size = 0;\r\n" -" ptr_record->frame_allocated = -1;\r\n" -" ptr_record->OOB_Flag = 0;\r\n" -" ptr_record->wc_heap_size_intra_frame = -1;\r\n" -" ptr_record->wc_heap_size_inter_frame = -1;\r\n" -"\r\n" -" index_record = Num_Records;\r\n" -" Num_Records++;\r\n" -" }\r\n" -"\r\n" -" /* Allocate memory block for the new record, add signature before the beginning and after the memory block and fill it with magic value */\r\n" -" ptr_record->block_ptr = mem_alloc_block( size, size_str );\r\n" -"\r\n" -" if ( ptr_record->block_ptr == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Error: Cannot Allocate Memory!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* Save all auxiliary information about the memory block */\r\n" -" strncpy( ptr_record->name, func_name, MAX_FUNCTION_NAME_LENGTH );\r\n" -" ptr_record->name[MAX_FUNCTION_NAME_LENGTH] = '\\0';\r\n" -" strncpy( ptr_record->params, size_str, MAX_PARAMS_LENGTH ); /* Note: The size string starts with either 'm' or 'c' to indicate 'm'alloc or 'c'alloc */\r\n" -" ptr_record->params[MAX_PARAMS_LENGTH] = '\\0';\r\n" -" ptr_record->lineno = func_lineno;\r\n" -" ptr_record->block_size = size;\r\n" -" ptr_record->total_block_size += size;\r\n" -"\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -" /* Export heap memory allocation record to the .csv file */\r\n" -" fprintf( fid_csv_filename, \"A,%d,%s,%d,%d\\n\", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size );\r\n" -"#endif\r\n" -"\r\n" -" if ( ptr_record->frame_allocated != -1 )\r\n" -" {\r\n" -" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Error: Attempt to Allocate the Same Memory Block with Freeing it First!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" ptr_record->frame_allocated = update_cnt; /* Store the current frame number -> later it will be used to determine the total duration */\r\n" -"\r\n" -" /* Update Heap Size in the current frame */\r\n" -" current_heap_size += ptr_record->block_size;\r\n" -"\r\n" -" /* Check, if this is the new Worst-Case RAM (stack + heap) */\r\n" -" current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) );\r\n" -" if ( current_stack_size + current_heap_size > wc_ram_size )\r\n" -" {\r\n" -" wc_ram_size = current_stack_size + current_heap_size;\r\n" -" wc_ram_frame = update_cnt;\r\n" -" }\r\n" -"\r\n" -" /* Add new entry to the heap allocation call tree */\r\n" -" if ( heap_allocation_call_tree == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Error: Heap allocation call tree not created!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* check, if the maximum size of the call tree has been reached -> resize if so */\r\n" -" if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size )\r\n" -" {\r\n" -" heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" /* push new entry (positive number means push op, neagtive number means pop op; zero index must be converted to 0.01 :-) */\r\n" -" heap_allocation_call_tree[heap_allocation_call_tree_size++] = index_record;\r\n" -"\r\n" -" return ptr_record->block_ptr;\r\n" -"}\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * mem_alloc_block()\r\n" -" *\r\n" -" * Physical allocation of memory using malloc(). Appends 'signature' before and after the block,\r\n" -" * pre-fills memory block with magic value\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"static void *mem_alloc_block( size_t size, const char *size_str )\r\n" -"{\r\n" -" size_t rounded_size;\r\n" -" void *block_ptr;\r\n" -" char *tmp_ptr;\r\n" -" size_t n, f;\r\n" -" int32_t fill_value;\r\n" -" int32_t *ptr32;\r\n" -" int32_t mask, temp;\r\n" -"\r\n" -" /* Round Up Block Size */\r\n" -" rounded_size = ROUND_BLOCK_SIZE( size );\r\n" -"\r\n" -" /* Allocate memory using the standard malloc() by adding room for Signature Values */\r\n" -" block_ptr = malloc( rounded_size + BLOCK_ROUNDING * 2 );\r\n" -"\r\n" -" if ( block_ptr == NULL )\r\n" -" {\r\n" -" return NULL;\r\n" -" }\r\n" -"\r\n" -" /* Add Signature Before the Start of the Block */\r\n" -" ptr32 = (int32_t *) block_ptr;\r\n" -" n = N_32BITS_BLOCKS;\r\n" -" do\r\n" -" {\r\n" -" *ptr32++ = MAGIC_VALUE_OOB;\r\n" -" } while ( --n );\r\n" -"\r\n" -" /* Fill Memory Block with Magic Value or 0 */\r\n" -" fill_value = MAGIC_VALUE_USED;\r\n" -" if ( size_str[0] == 'c' )\r\n" -" {\r\n" -" fill_value = 0x00000000;\r\n" -" }\r\n" -" n = size / sizeof( int32_t );\r\n" -" while ( n-- )\r\n" -" {\r\n" -" *ptr32++ = fill_value;\r\n" -" }\r\n" -"\r\n" -" /* Fill the Reminder of the Memory Block - After Rounding */\r\n" -" n = rounded_size - size;\r\n" -" f = n % sizeof( int32_t );\r\n" -" if ( f != 0 )\r\n" -" {\r\n" -" /* when filling with '0' need to adapt the magic value */\r\n" -" /* shift by [1->24, 2->16, 3->8] */\r\n" -" mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); /* (1) */\r\n" -" temp = MAGIC_VALUE_OOB & mask;\r\n" -" if ( fill_value != 0x0 )\r\n" -" { /* for malloc merge fill value */\r\n" -" temp += ( ~mask ) & MAGIC_VALUE_USED;\r\n" -" } /* for calloc the code in (1) above already introduces zeros */\r\n" -" *ptr32++ = temp;\r\n" -" }\r\n" -" n /= sizeof( int32_t );\r\n" -" n += N_32BITS_BLOCKS;\r\n" -"\r\n" -" /* Add Signature After the End of Block */\r\n" -" do\r\n" -" {\r\n" -" *ptr32++ = MAGIC_VALUE_OOB;\r\n" -" } while ( --n );\r\n" -"\r\n" -" /* Adjust the Memory Block Pointer (Magic Value Before and After the Memory Block Requested) */\r\n" -" tmp_ptr = (char *) block_ptr;\r\n" -" tmp_ptr += BLOCK_ROUNDING;\r\n" -" block_ptr = (void *) tmp_ptr;\r\n" -"\r\n" -" return block_ptr;\r\n" -"}\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * mem_set_usage()\r\n" -" *\r\n" -" * Calculates actual usage of memory block by checking the magic value that was used to pre-fill\r\n" -" * each memory block during its allocation\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"static int mem_set_usage( allocator_record *record_ptr )\r\n" -"{\r\n" -" int total_bytes_used;\r\n" -"\r\n" -" size_t n;\r\n" -" int32_t *ptr32;\r\n" -" char *ptr8;\r\n" -" size_t total_bytes;\r\n" -" int32_t fill_value;\r\n" -"\r\n" -" fill_value = MAGIC_VALUE_USED;\r\n" -" if ( ( record_ptr->params[0] ) == 'c' )\r\n" -" {\r\n" -" fill_value = 0x00000000;\r\n" -" }\r\n" -"\r\n" -" total_bytes = record_ptr->block_size;\r\n" -"\r\n" -" /* Check 4 bytes at a time */\r\n" -" ptr32 = (int32_t *) record_ptr->block_ptr;\r\n" -" total_bytes_used = 0;\r\n" -" for ( n = total_bytes / sizeof( int32_t ); n > 0; n-- )\r\n" -" {\r\n" -" if ( *ptr32++ != fill_value )\r\n" -" {\r\n" -" total_bytes_used += sizeof( int32_t );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Check remaining bytes (If Applicable) 1 byte at a time */\r\n" -" ptr8 = (char *) ptr32;\r\n" -" for ( n = total_bytes % sizeof( int32_t ); n > 0; n-- )\r\n" -" {\r\n" -" if ( *ptr8++ != (char) fill_value )\r\n" -" {\r\n" -" total_bytes_used++;\r\n" -" }\r\n" -"\r\n" -" /* Update Value */\r\n" -" fill_value >>= 8;\r\n" -" }\r\n" -"\r\n" -" return total_bytes_used;\r\n" -"}\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * mem_check_OOB()\r\n" -" *\r\n" -" * Checks, if out-of-bounds access has occured. This is done by inspecting the 'signature' value\r\n" -" * taht has been added before and after the memory block during its allocation\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"static unsigned int mem_check_OOB( allocator_record *record_ptr )\r\n" -"{\r\n" -" int32_t *ptr32;\r\n" -" unsigned int OOB_Flag = 0x0;\r\n" -" int32_t mask;\r\n" -" size_t i;\r\n" -" int f;\r\n" -"\r\n" -" ptr32 = (int32_t *) record_ptr->block_ptr - N_32BITS_BLOCKS;\r\n" -"\r\n" -" /* Check the Signature at the Beginning of Memory Block */\r\n" -" i = N_32BITS_BLOCKS;\r\n" -" do\r\n" -" {\r\n" -" if ( *ptr32++ ^ MAGIC_VALUE_OOB )\r\n" -" {\r\n" -" OOB_Flag |= OOB_START;\r\n" -" }\r\n" -" } while ( --i );\r\n" -"\r\n" -" /* Advance to End (Snap to lowest 32 Bits) */\r\n" -" ptr32 += record_ptr->block_size / sizeof( int32_t );\r\n" -"\r\n" -" /* Calculate Unused Space That has been added to get to the rounded Block Size */\r\n" -" i = ROUND_BLOCK_SIZE( record_ptr->block_size ) - record_ptr->block_size;\r\n" -"\r\n" -" /* Partial Check of Signature at the End of Memory Block (for block size that has been rounded) */\r\n" -" f = i % sizeof( int32_t );\r\n" -" if ( f != 0 )\r\n" -" {\r\n" -" mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 );\r\n" -" if ( ( *ptr32++ ^ MAGIC_VALUE_OOB ) & mask )\r\n" -" {\r\n" -" OOB_Flag |= OOB_END;\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Full Check of Signature at the End of Memory Block, i.e. all 32 Bits (for the remainder after rounding) */\r\n" -" i /= sizeof( int32_t );\r\n" -" i += N_32BITS_BLOCKS;\r\n" -" do\r\n" -" {\r\n" -" if ( *ptr32++ ^ MAGIC_VALUE_OOB )\r\n" -" {\r\n" -" OOB_Flag |= OOB_END;\r\n" -" }\r\n" -" } while ( --i );\r\n" -"\r\n" -" return OOB_Flag;\r\n" -"}\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * malloc_hash()\r\n" -" *\r\n" -" * Calculate hash from function name, line number and malloc size\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str )\r\n" -"{\r\n" -" unsigned long hash = 5381;\r\n" -" const char *ptr_str;\r\n" -"\r\n" -" ptr_str = func_name;\r\n" -" while ( ptr_str != NULL && *ptr_str != '\\0' )\r\n" -" {\r\n" -" hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */\r\n" -" }\r\n" -"\r\n" -" hash = ( ( hash << 5 ) + hash ) + func_lineno; /* hash * 33 + func_lineno */\r\n" -"\r\n" -" ptr_str = size_str;\r\n" -" while ( ptr_str != NULL && *ptr_str != '\\0' )\r\n" -" {\r\n" -" hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */\r\n" -" }\r\n" -"\r\n" -" return hash;\r\n" -"}\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * get_mem_record()\r\n" -" *\r\n" -" * Search for memory record in the internal list, return NULL if not found\r\n" -" * Start from index_record\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record )\r\n" -"{\r\n" -" int i;\r\n" -"\r\n" -" if ( *index_record < 0 || *index_record > Num_Records )\r\n" -" {\r\n" -" return NULL;\r\n" -" }\r\n" -"\r\n" -" /* calculate hash */\r\n" -" *hash = malloc_hash( func_name, func_lineno, size_str );\r\n" -"\r\n" -" for ( i = *index_record; i < Num_Records; i++ )\r\n" -" {\r\n" -" /* check, if memory block is not allocated at the moment and the hash matches */\r\n" -" if ( allocation_list[i].block_ptr == NULL && *hash == allocation_list[i].hash )\r\n" -" {\r\n" -" *index_record = i;\r\n" -" return &( allocation_list[i] );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* not found */\r\n" -" *index_record = -1;\r\n" -" return NULL;\r\n" -"}\r\n" -"\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * mem_free()\r\n" -" *\r\n" -" * This function de-allocatesd the memory block and frees the mphysical memory with free().\r\n" -" * It also updates actual and average usage of the memory block.\r\n" -" *\r\n" -" * Note: The record is not removed from the list and may be reused later on in mem_alloc()!\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"void mem_free( const char *func_name, int func_lineno, void *ptr )\r\n" -"{\r\n" -" int i, index_record;\r\n" -" char *tmp_ptr;\r\n" -" allocator_record *ptr_record;\r\n" -"\r\n" -" /* Search for the Block Pointer in the List */\r\n" -" ptr_record = NULL;\r\n" -" index_record = -1;\r\n" -" for ( i = 0; i < Num_Records; i++ )\r\n" -" {\r\n" -" if ( ptr == allocation_list[i].block_ptr )\r\n" -" { /* Yes, Found it */\r\n" -" ptr_record = &( allocation_list[i] );\r\n" -" index_record = i;\r\n" -" break;\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" if ( ptr_record == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Error: Unable to Find Record Corresponding to the Allocated Memory Block!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* Update the Heap Size */\r\n" -" current_heap_size -= ptr_record->block_size;\r\n" -"\r\n" -" /* Calculate the Actual Usage of the Memory Block (Look for Signature) */\r\n" -" ptr_record->total_used_size += mem_set_usage( ptr_record );\r\n" -"\r\n" -" /* Check, if Out-Of-Bounds Access has been Detected */\r\n" -" ptr_record->OOB_Flag = mem_check_OOB( ptr_record );\r\n" -"\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -" /* Export heap memory de-allocation record to the .csv file */\r\n" -" fprintf( fid_csv_filename, \"D,%d,%s,%d,%d\\n\", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size );\r\n" -"#endif\r\n" -"\r\n" -" /* De-Allocate Memory Block */\r\n" -" tmp_ptr = (char *) ptr;\r\n" -" tmp_ptr -= BLOCK_ROUNDING;\r\n" -" ptr = (void *) tmp_ptr;\r\n" -" free( ptr );\r\n" -"\r\n" -" /* Add new entry to the heap allocation call tree */\r\n" -" if ( heap_allocation_call_tree == NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Error: Heap allocation call tree not created!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -"\r\n" -" /* check, if the maximum size of the call tree has been reached -> resize if so */\r\n" -" if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size )\r\n" -" {\r\n" -" heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" heap_allocation_call_tree[heap_allocation_call_tree_size++] = -index_record;\r\n" -"\r\n" -" /* Reset memory block pointer (this is checked when updating wc intra-frame and inter-frame memory) */\r\n" -" ptr_record->block_ptr = NULL;\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * update_mem()\r\n" -" *\r\n" -" * This function updates the worst-case intra-frame memory and the worst-case inter-frame memory.\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"void update_mem( void )\r\n" -"{\r\n" -" int i, j, flag_alloc = -1, i_record;\r\n" -" int size_current_intra_frame_heap;\r\n" -" int *list_current_intra_frame_heap = NULL, n_items_current_intra_frame_heap;\r\n" -" allocator_record *ptr_record;\r\n" -"\r\n" -" /* process the heap allocation call tree and prepare lists of intra-frame and inter-frame heap memory blocks for this frame */\r\n" -" n_items_current_intra_frame_heap = 0;\r\n" -" size_current_intra_frame_heap = 0;\r\n" -" for ( i = 0; i < heap_allocation_call_tree_size; i++ )\r\n" -" {\r\n" -" /* get the record */\r\n" -" i_record = heap_allocation_call_tree[i];\r\n" -"\r\n" -" if ( i_record > 0 )\r\n" -" {\r\n" -" flag_alloc = 1;\r\n" -" }\r\n" -" else if ( i_record < 0 )\r\n" -" {\r\n" -" flag_alloc = 0;\r\n" -" i_record = -i_record;\r\n" -" }\r\n" -" ptr_record = &( allocation_list[i_record] );\r\n" -"\r\n" -" if ( ptr_record->frame_allocated == update_cnt && ptr_record->block_ptr == NULL )\r\n" -" {\r\n" -" /* intra-frame heap memory */\r\n" -" if ( list_current_intra_frame_heap == NULL )\r\n" -" {\r\n" -" list_current_intra_frame_heap = (int *) malloc( heap_allocation_call_tree_size * sizeof( int ) );\r\n" -" memset( list_current_intra_frame_heap, -1, heap_allocation_call_tree_size * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */\r\n" -" if ( i_record == 0 )\r\n" -" {\r\n" -" flag_alloc = 1;\r\n" -" for ( j = 0; j < n_items_current_intra_frame_heap; j++ )\r\n" -" {\r\n" -" if ( list_current_intra_frame_heap[j] == i_record )\r\n" -" {\r\n" -" flag_alloc = 0;\r\n" -" break;\r\n" -" }\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" if ( flag_alloc )\r\n" -" {\r\n" -" /* add to list */\r\n" -" list_current_intra_frame_heap[n_items_current_intra_frame_heap++] = i_record;\r\n" -" size_current_intra_frame_heap += ptr_record->block_size;\r\n" -"\r\n" -" /* no need to re-size the list -> the initially allocated size should be large enough */\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" /* remove from list */\r\n" -" for ( j = 0; j < n_items_current_intra_frame_heap; j++ )\r\n" -" {\r\n" -" if ( list_current_intra_frame_heap[j] == i_record )\r\n" -" {\r\n" -" break;\r\n" -" }\r\n" -" }\r\n" -" memmove( &list_current_intra_frame_heap[j], &list_current_intra_frame_heap[j + 1], ( n_items_current_intra_frame_heap - j ) * sizeof( int ) );\r\n" -" n_items_current_intra_frame_heap--;\r\n" -" size_current_intra_frame_heap -= ptr_record->block_size;\r\n" -"\r\n" -" /* reset block size */\r\n" -" ptr_record->frame_allocated = -1;\r\n" -" ptr_record->block_size = 0;\r\n" -" }\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" /* inter-frame heap memory */\r\n" -"\r\n" -" /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */\r\n" -" if ( i_record == 0 )\r\n" -" {\r\n" -" flag_alloc = 1;\r\n" -" for ( j = 0; j < n_items_current_inter_frame_heap; j++ )\r\n" -" {\r\n" -" if ( list_current_inter_frame_heap[j] == i_record )\r\n" -" {\r\n" -" flag_alloc = 0;\r\n" -" break;\r\n" -" }\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" if ( flag_alloc )\r\n" -" {\r\n" -" /* add to list */\r\n" -" if ( n_items_current_inter_frame_heap >= max_items_current_inter_frame_heap )\r\n" -" {\r\n" -" /* resize list, if needed */\r\n" -" max_items_current_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" list_current_inter_frame_heap = realloc( list_current_inter_frame_heap, max_items_current_inter_frame_heap * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" list_current_inter_frame_heap[n_items_current_inter_frame_heap++] = i_record;\r\n" -" size_current_inter_frame_heap += ptr_record->block_size;\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" /* remove from list */\r\n" -" for ( j = 0; j < n_items_current_inter_frame_heap; j++ )\r\n" -" {\r\n" -" if ( list_current_inter_frame_heap[j] == i_record )\r\n" -" {\r\n" -" break;\r\n" -" }\r\n" -" }\r\n" -" memmove( &list_current_inter_frame_heap[j], &list_current_inter_frame_heap[j + 1], ( n_items_current_inter_frame_heap - j ) * sizeof( int ) );\r\n" -" n_items_current_inter_frame_heap--;\r\n" -" size_current_inter_frame_heap -= ptr_record->block_size;\r\n" -"\r\n" -" /* reset block size */\r\n" -" ptr_record->frame_allocated = -1;\r\n" -" ptr_record->block_size = 0;\r\n" -" }\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* check, if this is the new worst-case for intra-frame heap memory */\r\n" -" if ( size_current_intra_frame_heap > size_wc_intra_frame_heap )\r\n" -" {\r\n" -" if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap )\r\n" -" {\r\n" -" /* resize the list, if needed */\r\n" -" max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" /* copy current-frame list to worst-case list */\r\n" -" memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) );\r\n" -" n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap;\r\n" -" size_wc_intra_frame_heap = size_current_intra_frame_heap;\r\n" -" location_wc_intra_frame_heap = update_cnt;\r\n" -"\r\n" -" /* update the wc numbers in all individual records */\r\n" -" for ( i = 0; i < n_items_wc_intra_frame_heap; i++ )\r\n" -" {\r\n" -" i_record = list_wc_intra_frame_heap[i];\r\n" -" ptr_record = &( allocation_list[i_record] );\r\n" -" ptr_record->wc_heap_size_intra_frame = ptr_record->block_size;\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* check, if this is the new worst-case for inter-frame heap memory */\r\n" -" if ( size_current_inter_frame_heap > size_wc_inter_frame_heap )\r\n" -" {\r\n" -" if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap )\r\n" -" {\r\n" -" /* resize list, if needed */\r\n" -" max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n" -" list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) );\r\n" -" }\r\n" -"\r\n" -" /* copy current-frame list to worst-case list */\r\n" -" memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) );\r\n" -" n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap;\r\n" -" size_wc_inter_frame_heap = size_current_inter_frame_heap;\r\n" -" location_wc_inter_frame_heap = update_cnt;\r\n" -"\r\n" -" /* update the wc numbers in all individual records */\r\n" -" for ( i = 0; i < n_items_wc_inter_frame_heap; i++ )\r\n" -" {\r\n" -" i_record = list_wc_inter_frame_heap[i];\r\n" -" ptr_record = &( allocation_list[i_record] );\r\n" -" ptr_record->wc_heap_size_inter_frame = ptr_record->block_size;\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* reset heap allocation call tree */\r\n" -" heap_allocation_call_tree_size = 0;\r\n" -"\r\n" -" /* de-allocate list of intra-frame heap memory blocks in the current fraeme - it's needed only inside this function */\r\n" -" if ( list_current_intra_frame_heap )\r\n" -" {\r\n" -" free( list_current_intra_frame_heap );\r\n" -" }\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * subst()\r\n" -" *\r\n" -" * Substitute character in string\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"static void subst( char *s, char from, char to )\r\n" -"{\r\n" -" while ( *s == from )\r\n" -" {\r\n" -" *s++ = to;\r\n" -" }\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * mem_count_summary()\r\n" -" *\r\n" -" * Print detailed (per-item) information about heap memory usage\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"static void mem_count_summary( void )\r\n" -"{\r\n" -" int i, j, index, index_record;\r\n" -" size_t length;\r\n" -" char buf[300], format_str[50], name_str[MAX_FUNCTION_NAME_LENGTH + 3], parms_str[MAX_PARAMS_LENGTH + 1], type_str[10], usage_str[20], size_str[20], line_str[10];\r\n" -" allocator_record *ptr_record, *ptr;\r\n" -"\r\n" -" /* Prepare format string */\r\n" -" sprintf( format_str, \"%%-%ds %%5s %%6s %%-%ds %%20s %%6s \", MAX_FUNCTION_NAME_LENGTH, MAX_PARAMS_LENGTH );\r\n" -"\r\n" -" if ( n_items_wc_intra_frame_heap > 0 )\r\n" -" {\r\n" -" /* Intra-Frame Heap Size */\r\n" -" fprintf( stdout, \"\\nList of memory blocks when maximum intra-frame heap size is reached:\\n\\n\" );\r\n" -"\r\n" -" /* Find duplicate records (same hash and worst-case heap size) */\r\n" -" for ( i = 0; i < n_items_wc_intra_frame_heap; i++ )\r\n" -" {\r\n" -" index_record = list_wc_intra_frame_heap[i];\r\n" -" if ( index_record == -1 )\r\n" -" {\r\n" -" continue;\r\n" -" }\r\n" -"\r\n" -" ptr_record = &( allocation_list[index_record] );\r\n" -" for ( j = i + 1; j < n_items_wc_intra_frame_heap; j++ )\r\n" -" {\r\n" -" index = list_wc_intra_frame_heap[j];\r\n" -" if ( index == -1 )\r\n" -" {\r\n" -" continue;\r\n" -" }\r\n" -" ptr = &( allocation_list[index] );\r\n" -"\r\n" -" if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_intra_frame == ptr_record->wc_heap_size_intra_frame )\r\n" -" {\r\n" -" ptr_record->noccurances++;\r\n" -" list_wc_intra_frame_heap[j] = -1;\r\n" -" }\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Print Header */\r\n" -" sprintf( buf, format_str, \"Function Name\", \"Line\", \"Type\", \"Function Parameters\", \"Maximum Size\", \"Usage\" );\r\n" -" puts( buf );\r\n" -" length = strlen( buf );\r\n" -" sprintf( buf, \"%0*d\\n\", (int) length - 1, 0 );\r\n" -" subst( buf, '0', '-' );\r\n" -" puts( buf );\r\n" -"\r\n" -" for ( i = 0; i < n_items_wc_intra_frame_heap; i++ )\r\n" -" {\r\n" -" index_record = list_wc_intra_frame_heap[i];\r\n" -"\r\n" -" if ( index_record != -1 )\r\n" -" {\r\n" -" /* get the record */\r\n" -" ptr_record = &( allocation_list[index_record] );\r\n" -"\r\n" -" /* prepare information strings */\r\n" -" strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH );\r\n" -" strcat( name_str, \"()\" );\r\n" -" name_str[MAX_FUNCTION_NAME_LENGTH] = '\\0';\r\n" -" strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH );\r\n" -" parms_str[MAX_PARAMS_LENGTH] = '\\0';\r\n" -"\r\n" -" if ( ptr_record->params[0] == 'm' )\r\n" -" {\r\n" -" strcpy( type_str, \"malloc\" );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" strcpy( type_str, \"calloc\" );\r\n" -" }\r\n" -"\r\n" -" sprintf( line_str, \"%d\", ptr_record->lineno );\r\n" -"\r\n" -" /* prepare average usage & memory size strings */\r\n" -" sprintf( usage_str, \"%d%%\", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 1 ) ) * 100.0f ) );\r\n" -"\r\n" -" if ( ptr_record->noccurances > 1 )\r\n" -" {\r\n" -" sprintf( size_str, \"%dx%d %s\", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" sprintf( size_str, \"%d %s\", (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n" -" }\r\n" -"\r\n" -" sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str );\r\n" -" puts( buf );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" fprintf( stdout, \"\\n\" );\r\n" -" }\r\n" -"\r\n" -" if ( n_items_wc_inter_frame_heap > 0 )\r\n" -" {\r\n" -" /* Inter-Frame Heap Size */\r\n" -" fprintf( stdout, \"\\nList of memory blocks when maximum inter-frame heap size is reached:\\n\\n\" );\r\n" -"\r\n" -" /* Find duplicate records (same hash and worst-case heap size) */\r\n" -" for ( i = 0; i < n_items_wc_inter_frame_heap; i++ )\r\n" -" {\r\n" -" index_record = list_wc_inter_frame_heap[i];\r\n" -" if ( index_record == -1 )\r\n" -" {\r\n" -" continue;\r\n" -" }\r\n" -" ptr_record = &( allocation_list[index_record] );\r\n" -" ptr_record->noccurances = 1; /* reset the counter as some blocks may have been both, intra-frame and inter-frame */\r\n" -" for ( j = i + 1; j < n_items_wc_inter_frame_heap; j++ )\r\n" -" {\r\n" -" index = list_wc_inter_frame_heap[j];\r\n" -" if ( index == -1 )\r\n" -" {\r\n" -" continue;\r\n" -" }\r\n" -" ptr = &( allocation_list[index] );\r\n" -"\r\n" -" if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_inter_frame == ptr_record->wc_heap_size_inter_frame )\r\n" -" {\r\n" -" ptr_record->noccurances++;\r\n" -" list_wc_inter_frame_heap[j] = -1;\r\n" -" }\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" /* Print Header */\r\n" -" sprintf( buf, format_str, \"Function Name\", \"Line\", \"Type\", \"Function Parameters\", \"Memory Size\", \"Usage\" );\r\n" -" puts( buf );\r\n" -" length = strlen( buf );\r\n" -" sprintf( buf, \"%0*d\\n\", (int) length - 1, 0 );\r\n" -" subst( buf, '0', '-' );\r\n" -" puts( buf );\r\n" -"\r\n" -" for ( i = 0; i < n_items_wc_inter_frame_heap; i++ )\r\n" -" {\r\n" -" index_record = list_wc_inter_frame_heap[i];\r\n" -"\r\n" -" if ( index_record != -1 )\r\n" -" {\r\n" -" /* get the record */\r\n" -" ptr_record = &( allocation_list[index_record] );\r\n" -"\r\n" -" /* prepare information strings */\r\n" -" strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH );\r\n" -" strcat( name_str, \"()\" );\r\n" -" name_str[MAX_FUNCTION_NAME_LENGTH] = '\\0';\r\n" -" strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH );\r\n" -" parms_str[MAX_PARAMS_LENGTH] = '\\0';\r\n" -"\r\n" -" if ( ptr_record->params[0] == 'm' )\r\n" -" {\r\n" -" strcpy( type_str, \"malloc\" );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" strcpy( type_str, \"calloc\" );\r\n" -" }\r\n" -"\r\n" -" sprintf( line_str, \"%d\", ptr_record->lineno );\r\n" -"\r\n" -" /* prepare average usage & memory size strings */\r\n" -" sprintf( usage_str, \"%d%%\", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 0.1f ) ) * 100.0f + 0.5f ) );\r\n" -"\r\n" -" if ( ptr_record->noccurances > 1 )\r\n" -" {\r\n" -" sprintf( size_str, \"%dx%d %s\", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" sprintf( size_str, \"%d %s\", (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n" -" }\r\n" -"\r\n" -" sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str );\r\n" -" puts( buf );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" fprintf( stdout, \"\\n\" );\r\n" -" }\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"#endif\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * print_mem()\r\n" -" *\r\n" -" * Print information about ROM and RAM memory usage\r\n" -" *--------------------------------------------------------------------*/\r\n" -"\r\n" -"void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] )\r\n" -"{\r\n" -" int i, nElem;\r\n" -"\r\n" -" fprintf( stdout, \"\\n\\n --- Memory usage --- \\n\\n\" );\r\n" -"\r\n" -" if ( Const_Data_PROM_Table != NULL )\r\n" -" {\r\n" -" nElem = 0;\r\n" -" while ( strcmp( Const_Data_PROM_Table[nElem].file_spec, \"\" ) != 0 )\r\n" -" nElem++;\r\n" -"\r\n" -" for ( i = 0; i < nElem; i++ )\r\n" -" {\r\n" -" if ( Stat_Cnt_Size > 0 )\r\n" -" {\r\n" -" /* words */\r\n" -" fprintf( stdout, \"Program ROM size (%s): %d words\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" /* bytes (here, we assume that each instruction takes PROM_INST_SIZE bits of the PROM memory) */\r\n" -" fprintf( stdout, \"Program ROM size (%s): %d bytes\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size * ( PROM_INST_SIZE / 8 ) );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" for ( i = 0; i < nElem; i++ )\r\n" -" {\r\n" -" if ( Const_Data_PROM_Table[i].Get_Const_Data_Size_Func == NULL )\r\n" -" {\r\n" -" fprintf( stdout, \"Error: Cannot retrieve or calculate Table ROM size of (%s)!\\n\", Const_Data_PROM_Table[i].file_spec );\r\n" -" }\r\n" -"\r\n" -" fprintf( stdout, \"Table ROM (const data) size (%s): %d %s\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].Get_Const_Data_Size_Func() >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] );\r\n" -" }\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" fprintf( stdout, \"Program ROM size: not available\\n\" );\r\n" -" fprintf( stdout, \"Table ROM (const data) size: not available\\n\" );\r\n" -" }\r\n" -"\r\n" -" if ( wc_ram_size > 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"Maximum RAM (stack + heap) size: %d %s in frame %d\\n\", wc_ram_size >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], wc_ram_frame );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" fprintf( stdout, \"Maximum RAM (stack + heap) size: not available\\n\" );\r\n" -" }\r\n" -"\r\n" -" /* check, if the stack is empty */\r\n" -" if ( ptr_current_stack != ptr_base_stack )\r\n" -" {\r\n" -" fprintf( stderr, \"Warning: Stack is not empty.\\n\" );\r\n" -" }\r\n" -"\r\n" -" if ( ptr_base_stack - ptr_max_stack > 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"Maximum stack size: %lu %s in frame %d\\n\", ( ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size],\r\n" -" wc_stack_frame );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" fprintf( stdout, \"Maximum stack size: not available\\n\" );\r\n" -" }\r\n" -"\r\n" -" /* last update of intra-frame memory and inter-frame memory, if needed */\r\n" -" if ( heap_allocation_call_tree_size > 0 )\r\n" -" {\r\n" -" update_mem();\r\n" -" }\r\n" -"\r\n" -" /* check, if all memory blocks have been deallocated (freed) */\r\n" -" for ( i = 0; i < Num_Records; i++ )\r\n" -" {\r\n" -" if ( allocation_list[i].block_ptr != NULL )\r\n" -" {\r\n" -" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", allocation_list[i].name, allocation_list[i].lineno, \"Error: Memory Block has not been De-Allocated with free()!\" );\r\n" -" exit( -1 );\r\n" -" }\r\n" -" }\r\n" -"\r\n" -" if ( n_items_wc_intra_frame_heap > 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"Maximum intra-frame heap size: %d %s in frame %d\\n\", size_wc_intra_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_intra_frame_heap );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" fprintf( stdout, \"Maximum intra-frame heap size: 0\\n\" );\r\n" -" }\r\n" -"\r\n" -" if ( n_items_wc_inter_frame_heap > 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"Maximum inter-frame heap size: %d %s in frame %d\\n\", size_wc_inter_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_inter_frame_heap );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" fprintf( stdout, \"Maximum inter-frame heap size: 0\\n\" );\r\n" -" }\r\n" -"\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -" /* Print detailed information about worst-case stack usage */\r\n" -" if ( ptr_base_stack - ptr_max_stack > 0 )\r\n" -" {\r\n" -" print_stack_call_tree();\r\n" -" }\r\n" -"\r\n" -" /* Print detailed information about worst-case heap usage */\r\n" -" mem_count_summary();\r\n" -"#endif\r\n" -"\r\n" -" if ( Stat_Cnt_Size > 0 )\r\n" -" {\r\n" -" /* words */\r\n" -" fprintf( stdout, \"\\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bits\\n\", 8 << Stat_Cnt_Size );\r\n" -" }\r\n" -" else\r\n" -" {\r\n" -" /* bytes */\r\n" -" fprintf( stdout, \"\\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bits\\n\", PROM_INST_SIZE );\r\n" -" }\r\n" -" fprintf( stdout, \"Note: The Data ROM size is calculated using the sizeof(type) built-in function\\n\" );\r\n" -"\r\n" -" if ( n_items_wc_intra_frame_heap > 0 )\r\n" -" {\r\n" -" fprintf( stdout, \"Intra-frame heap memory is allocated and de-allocated in the same frame\\n\" );\r\n" -" }\r\n" -"\r\n" -" /* De-allocate list of heap memory blocks */\r\n" -" if ( allocation_list != NULL )\r\n" -" {\r\n" -" free( allocation_list );\r\n" -" }\r\n" -"\r\n" -" /* De-allocate list of stack records */\r\n" -" if ( stack_callers[0] != NULL )\r\n" -" {\r\n" -" free( stack_callers[0] );\r\n" -" }\r\n" -"\r\n" -" if ( stack_callers[1] != NULL )\r\n" -" {\r\n" -" free( stack_callers[1] );\r\n" -" }\r\n" -"\r\n" -" /* De-allocate heap allocation call tree */\r\n" -" if ( heap_allocation_call_tree != NULL )\r\n" -" {\r\n" -" free( heap_allocation_call_tree );\r\n" -" }\r\n" -"\r\n" -" /* De-allocate intra-frame and inter-frame heap lists */\r\n" -" if ( list_wc_intra_frame_heap != NULL )\r\n" -" {\r\n" -" free( list_wc_intra_frame_heap );\r\n" -" }\r\n" -"\r\n" -" if ( list_current_inter_frame_heap != NULL )\r\n" -" {\r\n" -" free( list_current_inter_frame_heap );\r\n" -" }\r\n" -"\r\n" -" if ( list_wc_inter_frame_heap != NULL )\r\n" -" {\r\n" -" free( list_wc_inter_frame_heap );\r\n" -" }\r\n" -"\r\n" -"#ifdef MEM_COUNT_DETAILS\r\n" -" if ( fid_csv_filename != NULL )\r\n" -" {\r\n" -" fclose( fid_csv_filename );\r\n" -" }\r\n" -"#endif\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"#endif /* WMOPS */\r\n" -"\r\n" -"#ifndef WMOPS\r\n" -"int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */\r\n" -"#endif\r\n" -"\r\n" -"#ifdef WMOPS\r\n" -"/* Global counter for the calculation of BASOP complexity */\r\n" -"BASIC_OP *multiCounter = NULL;\r\n" -"int currCounter = 0;\r\n" -"int funcId_where_last_call_to_else_occurred;\r\n" -"long funcid_total_wmops_at_last_call_to_else;\r\n" -"int call_occurred = 1;\r\n" -"\r\n" -"BASIC_OP op_weight = {\r\n" -" 1, 1, 1, 1, 1,\r\n" -" 1, 1, 1, 1, 1,\r\n" -" 1, 1, 1, 1, 1,\r\n" -" 1, 1, 2, 2, 1,\r\n" -" 1, 1, 1, 3, 1,\r\n" -"\r\n" -" 1, 1, 1, 3, 1,\r\n" -" 4, 1, 18, 1, 1,\r\n" -" 2, 1, 2, 2, 1,\r\n" -" 1, 1, 1, 1, 1,\r\n" -" 3, 3, 3, 3, 1,\r\n" -"\r\n" -" 1, 1, 1, 1, 1,\r\n" -" 1, 1, 1, 2,\r\n" -" 1, 2, 2, 4, 1,\r\n" -" 1, 1, 1, 1, 1,\r\n" -" 1, 1, 1, 1, 1,\r\n" -"\r\n" -" 1, 1, 1, 1, 3,\r\n" -" 3, 3, 3, 3, 1,\r\n" -" 1, 1, 1, 1, 1,\r\n" -" 1, 1, 1, 4, 4,\r\n" -" 4, 8, 3, 4, 4,\r\n" -"\r\n" -" 5, 32, 3\r\n" -"};\r\n" -"\r\n" -"/* Set the counter group to use, default is zero */\r\n" -"void Set_BASOP_WMOPS_counter( int counterId )\r\n" -"{\r\n" -" if ( ( counterId > num_wmops_records ) || ( counterId < 0 ) )\r\n" -" {\r\n" -" currCounter = 0;\r\n" -" return;\r\n" -" }\r\n" -" currCounter = counterId;\r\n" -" call_occurred = 1;\r\n" -"}\r\n" -"\r\n" -"extern int32_t frame;\r\n" -"\r\n" -"long TotalWeightedOperation()\r\n" -"{\r\n" -" int i;\r\n" -" unsigned int *ptr, *ptr2;\r\n" -" long tot; \r\n" -"\r\n" -" tot = 0;\r\n" -" ptr = (unsigned int *) &multiCounter[currCounter];\r\n" -" ptr2 = (unsigned int *) &op_weight;\r\n" -"\r\n" -" for ( i = 0; i < ( int )( sizeof( multiCounter[currCounter] ) / sizeof( unsigned int ) ); i++ )\r\n" -" {\r\n" -" tot += ( ( *ptr++ ) * ( *ptr2++ ) );\r\n" -" }\r\n" -"\r\n" -" return ( tot );\r\n" -"}\r\n" -"\r\n" -"long DeltaWeightedOperation( void )\r\n" -"{\r\n" -" long NewWOper, delta;\r\n" -"\r\n" -" NewWOper = TotalWeightedOperation();\r\n" -"\r\n" -" delta = NewWOper - wmops[currCounter].LastWOper;\r\n" -" wmops[currCounter].LastWOper = NewWOper;\r\n" -"\r\n" -" return ( delta );\r\n" -"}\r\n" -"\r\n" -"/* Resets the current BASOP WMOPS counter */\r\n" -"void Reset_BASOP_WMOPS_counter( void )\r\n" -"{\r\n" -" int i;\r\n" -" long *ptr;\r\n" -"\r\n" -" /* clear the current BASOP operation counter before new frame begins */\r\n" -" ptr = (long *) &multiCounter[currCounter];\r\n" -" for ( i = 0; i < (int) ( sizeof( multiCounter[currCounter] ) / sizeof( long ) ); i++ )\r\n" -" {\r\n" -" *ptr++ = 0;\r\n" -" }\r\n" -"\r\n" -" wmops[currCounter].LastWOper = 0;\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"#endif\r\n" -"\r\n" -"\r\n" -"\r\n" \ No newline at end of file +"/*\r\n", +" * (C) 2022 copyright VoiceAge Corporation. All Rights Reserved.\r\n", +" *\r\n", +" * This software is protected by copyright law and by international treaties. The source code, and all of its derivations,\r\n", +" * is provided by VoiceAge Corporation under the \"ITU-T Software Tools' General Public License\". Please, read the license file\r\n", +" * or refer to ITU-T Recommendation G.191 on \"SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS\".\r\n", +" *\r\n", +" * Any use of this software is permitted provided that this notice is not removed and that neither the authors nor\r\n", +" * VoiceAge Corporation are deemed to have made any representations as to the suitability of this software\r\n", +" * for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE.\r\n", +" *\r\n", +" * Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca)\r\n", +" */\r\n", +"\r\n", +"#include \r\n", +"#include \r\n", +"#include \r\n", +"#include \r\n", +"#include \r\n", +"\r\n", +"#ifndef _MSC_VER\r\n", +"#include \r\n", +"#include \r\n", +"#else\r\n", +"#include \r\n", +"#endif\r\n", +"\r\n", +"#include \"options.h\"\r\n", +"#include \"wmc_auto.h\"\r\n", +"\r\n", +"#define WMC_TOOL_SKIP /* Skip the instrumentation of this file, if invoked by accident */\r\n", +"\r\n", +"#ifdef WMOPS\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * Complexity counting tool\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"#define MAX_FUNCTION_NAME_LENGTH 50 /* Maximum length of the function name */\r\n", +"#define MAX_PARAMS_LENGTH 50 /* Maximum length of the function parameter string */\r\n", +"#define MAX_NUM_RECORDS 300 /* Initial maximum number of records -> mightb be increased during runtime, if needed */\r\n", +"#define MAX_NUM_RECORDS_REALLOC_STEP 50 /* When re-allocating the list of records, increase the number of records by this number */\r\n", +"#define MAX_CALL_TREE_DEPTH 100 /* maximum depth of the function call tree */\r\n", +"#define DOUBLE_MAX 0x80000000\r\n", +"#define FAC ( FRAMES_PER_SECOND / 1e6 )\r\n", +"\r\n", +"\r\n", +"typedef struct \r\n", +"{\r\n", +" char label[MAX_FUNCTION_NAME_LENGTH];\r\n", +" long call_number;\r\n", +" long update_cnt;\r\n", +" int call_tree[MAX_CALL_TREE_DEPTH];\r\n", +" long LastWOper;\r\n", +" double start_selfcnt;\r\n", +" double current_selfcnt;\r\n", +" double max_selfcnt;\r\n", +" double min_selfcnt;\r\n", +" double tot_selfcnt;\r\n", +" double start_cnt; \r\n", +" double current_cnt;\r\n", +" double max_cnt;\r\n", +" double min_cnt;\r\n", +" double tot_cnt;\r\n", +"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n", +" int32_t current_call_number;\r\n", +" double wc_cnt;\r\n", +" double wc_selfcnt;\r\n", +" int32_t wc_call_number;\r\n", +"#endif\r\n", +"} wmops_record;\r\n", +"\r\n", +"double ops_cnt;\r\n", +"double prom_cnt;\r\n", +"double inst_cnt[NUM_INST];\r\n", +"\r\n", +"static wmops_record *wmops = NULL;\r\n", +"static int num_wmops_records, max_num_wmops_records;\r\n", +"static int current_record;\r\n", +"static long update_cnt;\r\n", +"static double start_cnt;\r\n", +"static double max_cnt;\r\n", +"static double min_cnt;\r\n", +"static double inst_cnt_wc[NUM_INST];\r\n", +"static long fnum_cnt_wc;\r\n", +"static int *wmops_caller_stack = NULL, wmops_caller_stack_index, max_wmops_caller_stack_index = 0;\r\n", +"static int *heap_allocation_call_tree = NULL, heap_allocation_call_tree_size = 0, heap_allocation_call_tree_max_size = 0;\r\n", +"\r\n", +"void reset_wmops( void )\r\n", +"{\r\n", +" int i, j;\r\n", +" unsigned int *ptr;\r\n", +"\r\n", +" num_wmops_records = 0;\r\n", +" max_num_wmops_records = MAX_NUM_RECORDS;\r\n", +" current_record = -1;\r\n", +" update_cnt = 0;\r\n", +"\r\n", +" max_cnt = 0.0;\r\n", +" min_cnt = DOUBLE_MAX;\r\n", +" start_cnt = 0.0;\r\n", +" ops_cnt = 0.0;\r\n", +"\r\n", +" /* allocate the list of wmops records */\r\n", +" if ( wmops == NULL )\r\n", +" {\r\n", +" wmops = (wmops_record *)malloc( max_num_wmops_records * sizeof( wmops_record ) );\r\n", +" }\r\n", +"\r\n", +" if ( wmops == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Error: Unable to Allocate List of WMOPS Records!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* allocate the BASOP WMOPS counter */\r\n", +" if ( multiCounter == NULL )\r\n", +" {\r\n", +" multiCounter = (BASIC_OP *) malloc( max_num_wmops_records * sizeof( BASIC_OP ) );\r\n", +" }\r\n", +"\r\n", +" if ( multiCounter == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Error: Unable to Allocate the BASOP WMOPS counter!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* initilize the list of wmops records */\r\n", +" /* initilize the BASOP WMOPS counters */\r\n", +" for ( i = 0; i < max_num_wmops_records; i++ )\r\n", +" {\r\n", +" strcpy( &wmops[i].label[0], \"\\0\" );\r\n", +" wmops[i].call_number = 0;\r\n", +" wmops[i].update_cnt = 0;\r\n", +" for ( j = 0; j < MAX_CALL_TREE_DEPTH; j++ )\r\n", +" {\r\n", +" wmops[i].call_tree[j] = -1;\r\n", +" }\r\n", +" wmops[i].start_selfcnt = 0.0;\r\n", +" wmops[i].current_selfcnt = 0.0;\r\n", +" wmops[i].max_selfcnt = 0.0;\r\n", +" wmops[i].min_selfcnt = DOUBLE_MAX;\r\n", +" wmops[i].tot_selfcnt = 0.0;\r\n", +" wmops[i].start_cnt = 0.0;\r\n", +" wmops[i].current_cnt = 0.0;\r\n", +" wmops[i].max_cnt = 0.0;\r\n", +" wmops[i].min_cnt = DOUBLE_MAX;\r\n", +" wmops[i].tot_cnt = 0.0;\r\n", +"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n", +" wmops[i].wc_cnt = 0.0;\r\n", +" wmops[i].wc_selfcnt = 0.0;\r\n", +" wmops[i].current_call_number = 0;\r\n", +" wmops[i].wc_call_number = -1;\r\n", +"#endif\r\n", +"\r\n", +" /* clear all BASOP operation counters */\r\n", +" ptr = (unsigned int*) &multiCounter[i];\r\n", +" for ( j = 0; j < (int) ( sizeof(BASIC_OP ) / sizeof( unsigned int ) ); j++ )\r\n", +" {\r\n", +" *ptr++ = 0;\r\n", +" }\r\n", +" wmops[i].LastWOper = 0;\r\n", +" }\r\n", +"\r\n", +" /* allocate the list of wmops callers to track the sequence of function calls */\r\n", +" wmops_caller_stack_index = 0;\r\n", +" max_wmops_caller_stack_index = MAX_NUM_RECORDS;\r\n", +" if ( wmops_caller_stack == NULL )\r\n", +" {\r\n", +" wmops_caller_stack = malloc( max_wmops_caller_stack_index * sizeof( int ) );\r\n", +" }\r\n", +"\r\n", +" if ( wmops_caller_stack == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Error: Unable to Allocate List of WMOPS Callers!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" for ( i = 0; i < max_wmops_caller_stack_index; i++ )\r\n", +" {\r\n", +" wmops_caller_stack[i] = -1;\r\n", +" }\r\n", +"\r\n", +" /* initialize auxiliary BASOP WMOPS variables */\r\n", +" call_occurred = 1;\r\n", +" funcId_where_last_call_to_else_occurred = INT_MAX;\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"\r\n", +"void push_wmops( const char *label )\r\n", +"{\r\n", +" int new_flag;\r\n", +" int i, j;\r\n", +"\r\n", +" /* Check, if this is a new function label */\r\n", +" new_flag = 1;\r\n", +" for ( i = 0; i < num_wmops_records; i++ )\r\n", +" {\r\n", +" if ( strcmp( wmops[i].label, label ) == 0 )\r\n", +" {\r\n", +" new_flag = 0;\r\n", +" break;\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* Create a new record in the list */\r\n", +" if ( new_flag )\r\n", +" {\r\n", +" if ( num_wmops_records >= max_num_wmops_records )\r\n", +" {\r\n", +" /* There is no room for a new wmops record -> reallocate the list */\r\n", +" max_num_wmops_records += MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" wmops = realloc( wmops, max_num_wmops_records * sizeof( wmops_record ) );\r\n", +" multiCounter = realloc( multiCounter, max_num_wmops_records * sizeof( BASIC_OP ) );\r\n", +" }\r\n", +"\r\n", +" strcpy( wmops[i].label, label );\r\n", +"\r\n", +" num_wmops_records++;\r\n", +" }\r\n", +"\r\n", +" /* Push the current context info to the new record */\r\n", +" if ( current_record >= 0 )\r\n", +" {\r\n", +" if ( wmops_caller_stack_index >= max_wmops_caller_stack_index )\r\n", +" {\r\n", +" /* There is no room for a new record -> reallocate the list */\r\n", +" max_wmops_caller_stack_index += MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" wmops_caller_stack = realloc( wmops_caller_stack, max_wmops_caller_stack_index * sizeof( int ) );\r\n", +" }\r\n", +" wmops_caller_stack[wmops_caller_stack_index++] = current_record;\r\n", +"\r\n", +" /* accumulate op counts */\r\n", +" wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt;\r\n", +"\r\n", +" /* update call tree */\r\n", +" for ( j = 0; j < MAX_CALL_TREE_DEPTH; j++ )\r\n", +" {\r\n", +" if ( wmops[i].call_tree[j] == current_record )\r\n", +" {\r\n", +" break;\r\n", +" }\r\n", +" else if ( wmops[i].call_tree[j] == -1 )\r\n", +" {\r\n", +" wmops[i].call_tree[j] = current_record;\r\n", +" break;\r\n", +" }\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* update the current context info */\r\n", +" current_record = i;\r\n", +" wmops[current_record].start_selfcnt = ops_cnt;\r\n", +" wmops[current_record].start_cnt = ops_cnt;\r\n", +" wmops[current_record].call_number++;\r\n", +"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n", +" wmops[current_record].current_call_number++;\r\n", +"#endif\r\n", +"\r\n", +" /* set the ID of BASOP functions counters */\r\n", +" Set_BASOP_WMOPS_counter( current_record );\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"\r\n", +"void pop_wmops( void )\r\n", +"{\r\n", +" long tot;\r\n", +"\r\n", +" /* Check for underflow */\r\n", +" if ( current_record < 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"\\r pop_wmops(): stack underflow, too many calls to pop_wmops()\\n\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* add the BASOP complexity to the counter */\r\n", +" tot = DeltaWeightedOperation();\r\n", +" ops_cnt += tot;\r\n", +"\r\n", +" /* update count of current record */\r\n", +" wmops[current_record].current_selfcnt += ops_cnt - wmops[current_record].start_selfcnt;\r\n", +" wmops[current_record].current_cnt += ops_cnt - wmops[current_record].start_cnt;\r\n", +"\r\n", +" /* Get back previous context from stack */\r\n", +" if ( wmops_caller_stack_index > 0 )\r\n", +" {\r\n", +" current_record = wmops_caller_stack[--wmops_caller_stack_index];\r\n", +" wmops[current_record].start_selfcnt = ops_cnt;\r\n", +"\r\n", +" /* set the ID of the previous BASOP counter */\r\n", +" Set_BASOP_WMOPS_counter( current_record );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" current_record = -1;\r\n", +" }\r\n", +"\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"\r\n", +"void update_wmops( void )\r\n", +"{\r\n", +" int i;\r\n", +" double current_cnt;\r\n", +"#ifdef WMOPS_PER_FRAME\r\n", +" static FILE *fid = NULL;\r\n", +" const char filename[] = \"wmops_analysis\";\r\n", +" float tmpF;\r\n", +"#endif\r\n", +"\r\n", +" if ( wmops_caller_stack_index != 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"update_wmops(): WMOPS caller stack corrupted - check that all push_wmops() are matched with pop_wmops()!\\n\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +"#ifdef WMOPS_PER_FRAME\r\n", +" /* Check, if the output file has already been opened */\r\n", +" if ( fid == NULL )\r\n", +" {\r\n", +" fid = fopen( filename, \"wb\" );\r\n", +"\r\n", +" if ( fid == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"\\nCannot open %s!\\n\\n\", filename );\r\n", +" exit( -1 );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* Write current complexity to the external file */\r\n", +" tmpF = (float) ( FAC * wmops[0].current_cnt );\r\n", +" fwrite( &tmpF, sizeof( float ), 1, fid );\r\n", +"#endif\r\n", +"\r\n", +"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n", +" if ( ops_cnt - start_cnt > max_cnt )\r\n", +" {\r\n", +" for ( i = 0; i < num_wmops_records; i++ )\r\n", +" {\r\n", +" wmops[i].wc_cnt = wmops[i].current_cnt;\r\n", +" wmops[i].wc_selfcnt = wmops[i].current_selfcnt;\r\n", +" wmops[i].wc_call_number = wmops[i].current_call_number;\r\n", +" }\r\n", +" }\r\n", +"#endif\r\n", +"\r\n", +" for ( i = 0; i < num_wmops_records; i++ )\r\n", +" {\r\n", +" wmops[i].tot_selfcnt += wmops[i].current_selfcnt;\r\n", +" wmops[i].tot_cnt += wmops[i].current_cnt;\r\n", +"\r\n", +" if ( wmops[i].current_selfcnt > 0 )\r\n", +" {\r\n", +" if ( wmops[i].current_selfcnt > wmops[i].max_selfcnt )\r\n", +" {\r\n", +" wmops[i].max_selfcnt = wmops[i].current_selfcnt;\r\n", +" }\r\n", +"\r\n", +" if ( wmops[i].current_selfcnt < wmops[i].min_selfcnt )\r\n", +" {\r\n", +" wmops[i].min_selfcnt = wmops[i].current_selfcnt;\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" wmops[i].current_selfcnt = 0;\r\n", +"\r\n", +" if ( wmops[i].current_cnt > 0 )\r\n", +" {\r\n", +" if ( wmops[i].current_cnt > wmops[i].max_cnt )\r\n", +" {\r\n", +" wmops[i].max_cnt = wmops[i].current_cnt;\r\n", +" }\r\n", +"\r\n", +"\r\n", +" if ( wmops[i].current_cnt < wmops[i].min_cnt )\r\n", +" {\r\n", +" wmops[i].min_cnt = wmops[i].current_cnt;\r\n", +" }\r\n", +"\r\n", +" wmops[i].update_cnt++;\r\n", +" }\r\n", +"\r\n", +" wmops[i].current_cnt = 0;\r\n", +"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n", +" wmops[i].current_call_number = 0;\r\n", +"#endif\r\n", +"\r\n", +" /* update the WC of all BASOP counters */\r\n", +" Set_BASOP_WMOPS_counter( i );\r\n", +" Reset_BASOP_WMOPS_counter();\r\n", +" }\r\n", +"\r\n", +" current_cnt = ops_cnt - start_cnt;\r\n", +" if ( current_cnt > max_cnt )\r\n", +" {\r\n", +" max_cnt = current_cnt;\r\n", +"\r\n", +" for ( i = 0; i < NUM_INST; i++ )\r\n", +" {\r\n", +" inst_cnt_wc[i] = inst_cnt[i];\r\n", +" }\r\n", +"\r\n", +" fnum_cnt_wc = update_cnt + 1;\r\n", +" }\r\n", +"\r\n", +" if ( current_cnt < min_cnt )\r\n", +" {\r\n", +" min_cnt = current_cnt;\r\n", +" }\r\n", +"\r\n", +" for ( i = 0; i < NUM_INST; i++ )\r\n", +" {\r\n", +" inst_cnt[i] = 0.0;\r\n", +" }\r\n", +"\r\n", +" start_cnt = ops_cnt;\r\n", +"\r\n", +" /* increment frame counter */\r\n", +" update_cnt++;\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"\r\n", +"void print_wmops( void )\r\n", +"{\r\n", +" int i, label_len, max_label_len;\r\n", +"\r\n", +" char *sfmts = \"%*s %8s %8s %7s %7s\\n\";\r\n", +" char *dfmts = \"%*s %8.2f %8.3f %7.3f %7.3f\\n\";\r\n", +" char *sfmt = \"%*s %8s %8s %7s %7s %7s %7s %7s\\n\";\r\n", +" char *dfmt = \"%*s %8.2f %8.3f %7.3f %7.3f %7.3f %7.3f %7.3f\\n\";\r\n", +"\r\n", +"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n", +" int j;\r\n", +" char *sfmtt = \"%20s %4s %15s\\n\";\r\n", +" char *dfmtt = \"%20s %4d \";\r\n", +"#endif\r\n", +"\r\n", +" /* calculate maximum label length for compact prinout */\r\n", +" max_label_len = 0;\r\n", +" for ( i = 0; i < num_wmops_records; i++ )\r\n", +" {\r\n", +" label_len = strlen( wmops[i].label );\r\n", +" if ( label_len > max_label_len )\r\n", +" {\r\n", +" max_label_len = label_len;\r\n", +" }\r\n", +" }\r\n", +" max_label_len += 4;\r\n", +"\r\n", +" fprintf( stdout, \"\\n\\n --- Complexity analysis [WMOPS] --- \\n\\n\" );\r\n", +" \r\n", +" fprintf( stdout, \"%*s %33s %23s\\n\", max_label_len, \"\", \"|------ SELF ------|\", \"|--- CUMULATIVE ---|\" );\r\n", +" fprintf( stdout, sfmt, max_label_len, \" routine\", \" calls\", \" min \", \" max \", \" avg \", \" min \", \" max \", \" avg \" );\r\n", +" fprintf( stdout, sfmt, max_label_len, \"---------------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\", \"------\" );\r\n", +"\r\n", +" for ( i = 0; i < num_wmops_records; i++ )\r\n", +" {\r\n", +" fprintf( stdout, dfmt, max_label_len, wmops[i].label, update_cnt == 0 ? 0 : (float) wmops[i].call_number / update_cnt,\r\n", +" wmops[i].min_selfcnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_selfcnt,\r\n", +" FAC * wmops[i].max_selfcnt,\r\n", +" wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_selfcnt / wmops[i].update_cnt,\r\n", +" wmops[i].min_cnt == DOUBLE_MAX ? 0 : FAC * wmops[i].min_cnt,\r\n", +" FAC * wmops[i].max_cnt,\r\n", +" wmops[i].update_cnt == 0 ? 0 : FAC * wmops[i].tot_cnt / wmops[i].update_cnt );\r\n", +" }\r\n", +"\r\n", +" fprintf( stdout, sfmts, max_label_len, \"---------------\", \"------\", \"------\", \"------\", \"------\" );\r\n", +" fprintf( stdout, dfmts, max_label_len, \"total\", (float) update_cnt, update_cnt == 0 ? 0 : FAC * min_cnt, FAC * max_cnt, update_cnt == 0 ? 0 : FAC * ops_cnt / update_cnt );\r\n", +" fprintf( stdout, \"\\n\" );\r\n", +"\r\n", +"#ifdef WMOPS_WC_FRAME_ANALYSIS\r\n", +" fprintf( stdout, \"\\nComplexity analysis for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc );\r\n", +" fprintf( stdout, \"%*s %8s %10s %12s\\n\", max_label_len, \" routine\", \" calls\", \" SELF\", \" CUMULATIVE\" );\r\n", +" fprintf( stdout, \"%*s %8s %10s %10s\\n\", max_label_len, \"---------------\", \"------\", \"------\", \"----------\" );\r\n", +"\r\n", +" for ( i = 0; i < num_wmops_records; i++ )\r\n", +" {\r\n", +" if ( wmops[i].wc_call_number > 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"%*s %8d %10.3f %12.3f\\n\", max_label_len, wmops[i].label, wmops[i].wc_call_number, FAC * wmops[i].wc_selfcnt, FAC * wmops[i].wc_cnt );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" fprintf( stdout, \"\\nCall tree for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc );\r\n", +" fprintf( stdout, sfmtt, \" function\", \"num\", \"called by \" );\r\n", +" fprintf( stdout, sfmtt, \"---------------\", \"---\", \"--------------\" );\r\n", +"\r\n", +" for ( i = 0; i < num_wmops_records; i++ )\r\n", +" {\r\n", +" if ( wmops[i].wc_call_number > 0 )\r\n", +" {\r\n", +" fprintf( stdout, dfmtt, wmops[i].label, i );\r\n", +" for ( j = 0; wmops[i].call_tree[j] != -1 && j < MAX_CALL_TREE_DEPTH; j++ )\r\n", +" {\r\n", +" if ( j != 0 )\r\n", +" {\r\n", +" fprintf( stdout, \", \" );\r\n", +" }\r\n", +" fprintf( stdout, \"%d\", wmops[i].call_tree[j] );\r\n", +" }\r\n", +" fprintf( stdout, \"\\n\" );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" fprintf( stdout, \"\\n\\n\" );\r\n", +"\r\n", +" fprintf( stdout, \"\\nInstruction type analysis for the worst-case frame %ld:\\n\\n\", fnum_cnt_wc ); \r\n", +" for ( i = 0; i < NUM_INST; i++ )\r\n", +" {\r\n", +" switch ( (enum instructions) i )\r\n", +" {\r\n", +" case _ADD:\r\n", +" fprintf( stdout, \"\\tAdds: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _ABS:\r\n", +" fprintf( stdout, \"\\tAbsolutes: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _MULT:\r\n", +" fprintf( stdout, \"\\tMultiplies: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _MAC:\r\n", +" fprintf( stdout, \"\\tMACs: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _MOVE:\r\n", +" fprintf( stdout, \"\\tMoves: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _STORE:\r\n", +" fprintf( stdout, \"\\tStores: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _LOGIC:\r\n", +" fprintf( stdout, \"\\tLogicals: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _SHIFT:\r\n", +" fprintf( stdout, \"\\tShifts: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _BRANCH:\r\n", +" fprintf( stdout, \"\\tBranches: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _DIV:\r\n", +" fprintf( stdout, \"\\tDivisions: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _SQRT:\r\n", +" fprintf( stdout, \"\\tSquare Root: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _TRANS:\r\n", +" fprintf( stdout, \"\\tTrans: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _FUNC:\r\n", +" fprintf( stdout, \"\\tFunc Call: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _LOOP:\r\n", +" fprintf( stdout, \"\\tLoop Init: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _INDIRECT:\r\n", +" fprintf( stdout, \"\\tIndirect Addr: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _PTR_INIT:\r\n", +" fprintf( stdout, \"\\tPointer Init: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _TEST:\r\n", +" fprintf( stdout, \"\\tExtra condit.: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _POWER:\r\n", +" fprintf( stdout, \"\\tExponential: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _LOG:\r\n", +" fprintf( stdout, \"\\tLogarithm: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" case _MISC:\r\n", +" fprintf( stdout, \"\\tAll other op.: %12.1f\\n\", inst_cnt_wc[i] );\r\n", +" break;\r\n", +" default:\r\n", +" fprintf( stdout, \"\\tERROR: Invalid instruction type: %d\\n\\n\", i );\r\n", +" }\r\n", +" }\r\n", +"#endif\r\n", +"\r\n", +" /* De-allocate the list of wmops record */\r\n", +" if ( wmops != NULL )\r\n", +" {\r\n", +" free( wmops );\r\n", +" }\r\n", +"\r\n", +" /* De-allocate the list of wmops caller functions */\r\n", +" if ( wmops_caller_stack != NULL )\r\n", +" {\r\n", +" free( wmops_caller_stack );\r\n", +" }\r\n", +"\r\n", +" /* De-allocate the BASOP WMOPS counter */\r\n", +" if ( multiCounter != NULL )\r\n", +" {\r\n", +" free( multiCounter );\r\n", +" }\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * Memory counting tool measuring RAM usage (stack and heap)\r\n", +" *\r\n", +" * Maximum RAM is measured by monitoring the total allocated memory (stack and heap) in each frame.\r\n", +" *\r\n", +" * Maximum stack is measured by monitoring the difference between the 'top' and 'bottom' of the stack. The 'bottom' of the stack is updated in each function\r\n", +" * with a macro 'func_start_' which is inserted automatically to all functions during the instrumentation process.\r\n", +" *\r\n", +" * Maximum heap is measured by summing the sizes of all memory blocks allocated by malloc() or calloc() and deallocated by free(). The maximum heap size is\r\n", +" * updated each time when the macros malloc_() or calloc_() is invoked. The macros 'malloc_ and calloc_' are inserted automatically during the instrumentation process.\r\n", +" * As part of heap measurements, intra-frame heap and inter-frame heap are measured separately. Intra-frame heap refers to heap memory which is allocated and deallocated\r\n", +" * within a single frame. Inter-frame heap, on the contrary, refers to heap memory which is reserved for more than one frame.\r\n", +" *\r\n", +" * In order to run the memory counting tool the function reset_mem(cnt_size) must be called at the beginning of the encoding/decoding process.\r\n", +" * The unit in which memory consumption is reported is set via the parameter 'cnt_size'. It can be set to 0 (bytes), 1 (32b words) or 2 (64b words).\r\n", +" * At the end of the encoding/decoding process, 'print_mem()' function may be called to print basic information about memory consumption. If the macro 'MEM_COUNT_DETAILS'\r\n", +" * is activated, detailed information is printed\r\n", +" *\r\n", +" * The macro 'WMOPS' needs to be activated to enable memory counting. To avoid the instrumentation of malloc()/calloc()/free() calls, use\r\n", +" * #define WMC_TOOL_SKIP ... #undef WMC_TOOL_SKIP macro pair around the malloc(), calloc() and free().\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"/* This is the value (in bytes) towards which the block size is rounded. For example, a block of 123 bytes, when using\r\n", +" a 32 bits system, will end up taking 124 bytes since the last unused byte cannot be used for another block. */\r\n", +"#ifdef MEM_ALIGN_64BITS\r\n", +"#define BLOCK_ROUNDING 8 /* Align on 64 Bits */\r\n", +"#else\r\n", +"#define BLOCK_ROUNDING 4 /* Align on 32 Bits */\r\n", +"#endif\r\n", +"\r\n", +"#define N_32BITS_BLOCKS ( BLOCK_ROUNDING / sizeof( int32_t ) )\r\n", +"#define ROUND_BLOCK_SIZE( n ) ( ( ( n ) + BLOCK_ROUNDING - 1 ) & ~( BLOCK_ROUNDING - 1 ) )\r\n", +"\r\n", +"#define MAGIC_VALUE_OOB 0x12A534F0 /* Signature value which is inserted before and after each allocated memory block, used to detect out-of-bound access */\r\n", +"#define MAGIC_VALUE_USED ( ~MAGIC_VALUE_OOB ) /* Value used to pre-fill allocated memory blocks, used to calculate actual memory usage */\r\n", +"#define OOB_START 0x1 /* Flag indicating out-of-bounds access before memory block */\r\n", +"#define OOB_END 0x2 /* Flag indicating out-of-bounds access after memory block */\r\n", +"\r\n", +"#ifdef MEM_COUNT_DETAILS\r\n", +"const char *csv_filename = \"mem_analysis.csv\";\r\n", +"static FILE *fid_csv_filename = NULL;\r\n", +"#endif\r\n", +"\r\n", +"typedef struct\r\n", +"{\r\n", +" char function_name[MAX_FUNCTION_NAME_LENGTH + 1];\r\n", +" int16_t *stack_ptr;\r\n", +"} caller_info;\r\n", +"\r\n", +"static caller_info *stack_callers[2] = {NULL, NULL};\r\n", +"\r\n", +"static int16_t *ptr_base_stack = 0; /* Pointer to the bottom of stack (base pointer). Stack grows up. */\r\n", +"static int16_t *ptr_current_stack = 0; /* Pointer to the current stack pointer */\r\n", +"static int16_t *ptr_max_stack = 0; /* Pointer to the maximum stack pointer (the farest point from the bottom of stack) */\r\n", +"static int32_t wc_stack_frame = 0; /* Frame corresponding to the worst-case stack usage */\r\n", +"static int current_calls = 0, max_num_calls = MAX_NUM_RECORDS;\r\n", +"static char location_max_stack[256] = \"undefined\";\r\n", +"\r\n", +"/* Heap-related variables */\r\n", +"typedef struct\r\n", +"{\r\n", +" char name[MAX_FUNCTION_NAME_LENGTH + 1]; /* +1 for NUL */\r\n", +" char params[1 + MAX_PARAMS_LENGTH + 1]; /* +1 for 'm'/'c' alloc & +1 for NUL */\r\n", +" unsigned long hash;\r\n", +" int lineno;\r\n", +" void *block_ptr;\r\n", +" int block_size;\r\n", +" unsigned long total_block_size; /* Cumulative sum of the allocated size in the session */\r\n", +" unsigned long total_used_size; /* Cumulative sum of the used size in the session */\r\n", +" int wc_heap_size_intra_frame; /* Worst-Case Intra-Frame Heap Size */\r\n", +" int wc_heap_size_inter_frame; /* Worst-Case Inter-Frame Heap Size */\r\n", +" int frame_allocated; /* Frame number in which the Memory Block has been allocated (-1 if not allocated at the moment) */\r\n", +" int OOB_Flag;\r\n", +" int noccurances; /* Number of times that the memory block has been allocated in a frame */\r\n", +"} allocator_record;\r\n", +"\r\n", +"allocator_record *allocation_list = NULL;\r\n", +"\r\n", +"static int Num_Records, Max_Num_Records;\r\n", +"static size_t Stat_Cnt_Size = USE_BYTES;\r\n", +"static const char *Count_Unit[] = { \"bytes\", \"words\", \"words\", \"words\" };\r\n", +"\r\n", +"static int32_t wc_ram_size, wc_ram_frame;\r\n", +"static int32_t current_heap_size;\r\n", +"static int *list_wc_intra_frame_heap, n_items_wc_intra_frame_heap, max_items_wc_intra_frame_heap, size_wc_intra_frame_heap, location_wc_intra_frame_heap;\r\n", +"static int *list_current_inter_frame_heap, n_items_current_inter_frame_heap, max_items_current_inter_frame_heap, size_current_inter_frame_heap;\r\n", +"static int *list_wc_inter_frame_heap, n_items_wc_inter_frame_heap, max_items_wc_inter_frame_heap, size_wc_inter_frame_heap, location_wc_inter_frame_heap;\r\n", +"\r\n", +"/* Local Functions */\r\n", +"static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str );\r\n", +"allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record );\r\n", +"static void *mem_alloc_block( size_t size, const char *size_str );\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * reset_mem()\r\n", +" *\r\n", +" * Initialize/reset memory counting tool (stack and heap)\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"void reset_mem( Counting_Size cnt_size )\r\n", +"{\r\n", +" int16_t something;\r\n", +" size_t tmp_size;\r\n", +"\r\n", +" /* initialize list of stack records */\r\n", +" if ( stack_callers[0] == NULL )\r\n", +" {\r\n", +" stack_callers[0] = malloc( MAX_NUM_RECORDS * sizeof( caller_info ) );\r\n", +" stack_callers[1] = malloc( MAX_NUM_RECORDS * sizeof( caller_info ) );\r\n", +" }\r\n", +"\r\n", +" if ( stack_callers[0] == NULL || stack_callers[1] == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Error: Unable to Allocate List of Stack Records!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" current_calls = 0;\r\n", +" max_num_calls = MAX_NUM_RECORDS;\r\n", +"\r\n", +" /* initialize stack pointers */\r\n", +" ptr_base_stack = &something;\r\n", +" ptr_max_stack = ptr_base_stack;\r\n", +" ptr_current_stack = ptr_base_stack;\r\n", +"\r\n", +" /* initialize the unit of memory block size */\r\n", +" Stat_Cnt_Size = cnt_size;\r\n", +"\r\n", +" /* Check, if sizeof(int32_t) is 4 bytes */\r\n", +" tmp_size = sizeof( int32_t );\r\n", +" if ( tmp_size != 4 )\r\n", +" {\r\n", +" fprintf( stderr, \"Error: Expecting 'int32_t' to be a 32 Bits Integer!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* create allocation list for malloc() memory blocks */\r\n", +" if ( allocation_list == NULL )\r\n", +" {\r\n", +" allocation_list = malloc( MAX_NUM_RECORDS * sizeof( allocator_record ) );\r\n", +" }\r\n", +"\r\n", +" if ( allocation_list == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Error: Unable to Create List of Memory Blocks!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" Num_Records = 0;\r\n", +" Max_Num_Records = MAX_NUM_RECORDS;\r\n", +"\r\n", +" wc_ram_size = 0;\r\n", +" wc_ram_frame = -1;\r\n", +" current_heap_size = 0;\r\n", +"\r\n", +" /* heap allocation tree */\r\n", +" heap_allocation_call_tree_max_size = MAX_NUM_RECORDS;\r\n", +" if ( heap_allocation_call_tree == NULL )\r\n", +" {\r\n", +" heap_allocation_call_tree = (int *) malloc( heap_allocation_call_tree_max_size * sizeof( int ) );\r\n", +" memset( heap_allocation_call_tree, -1, heap_allocation_call_tree_max_size * sizeof( int ) );\r\n", +" }\r\n", +" heap_allocation_call_tree_size = 0;\r\n", +"\r\n", +" /* wc intra-frame heap */\r\n", +" max_items_wc_intra_frame_heap = MAX_NUM_RECORDS;\r\n", +" if ( list_wc_intra_frame_heap == NULL )\r\n", +" {\r\n", +" list_wc_intra_frame_heap = (int *) malloc( max_items_wc_intra_frame_heap * sizeof( int ) );\r\n", +" memset( list_wc_intra_frame_heap, -1, max_items_wc_intra_frame_heap * sizeof( int ) );\r\n", +" }\r\n", +" n_items_wc_intra_frame_heap = 0;\r\n", +" size_wc_intra_frame_heap = 0;\r\n", +" location_wc_intra_frame_heap = -1;\r\n", +"\r\n", +" /* current inter-frame heap */\r\n", +" max_items_current_inter_frame_heap = MAX_NUM_RECORDS;\r\n", +" if ( list_current_inter_frame_heap == NULL )\r\n", +" {\r\n", +" list_current_inter_frame_heap = (int *) malloc( max_items_current_inter_frame_heap * sizeof( int ) );\r\n", +" memset( list_current_inter_frame_heap, -1, max_items_current_inter_frame_heap * sizeof( int ) );\r\n", +" }\r\n", +" n_items_current_inter_frame_heap = 0;\r\n", +" size_current_inter_frame_heap = 0;\r\n", +"\r\n", +" /* wc inter-frame heap */\r\n", +" max_items_wc_inter_frame_heap = MAX_NUM_RECORDS;\r\n", +" if ( list_wc_inter_frame_heap == NULL )\r\n", +" {\r\n", +" list_wc_inter_frame_heap = (int *) malloc( max_items_wc_inter_frame_heap * sizeof( int ) );\r\n", +" memset( list_wc_inter_frame_heap, -1, max_items_wc_inter_frame_heap * sizeof( int ) );\r\n", +" }\r\n", +" n_items_wc_inter_frame_heap = 0;\r\n", +" size_wc_inter_frame_heap = 0;\r\n", +" location_wc_inter_frame_heap = -1;\r\n", +"\r\n", +"#ifdef MEM_COUNT_DETAILS\r\n", +" /* Check, if the .csv file has already been opened */\r\n", +" if ( fid_csv_filename == NULL )\r\n", +" {\r\n", +" fid_csv_filename = fopen( csv_filename, \"wb\" );\r\n", +"\r\n", +" if ( fid_csv_filename == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"\\nCannot open %s!\\n\\n\", csv_filename );\r\n", +" exit( -1 );\r\n", +" }\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" /* reset file */\r\n", +" rewind( fid_csv_filename );\r\n", +" }\r\n", +"#endif\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * reset_stack()\r\n", +" *\r\n", +" * Reset stack pointer\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"void reset_stack( void )\r\n", +"{\r\n", +" int16_t something;\r\n", +"\r\n", +" /* initialize/reset stack pointers */\r\n", +" ptr_base_stack = &something;\r\n", +" ptr_max_stack = ptr_base_stack;\r\n", +" ptr_current_stack = ptr_base_stack;\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * push_stack()\r\n", +" *\r\n", +" * Check the current stack pointer and update the maximum stack pointer, if new maximum found.\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"int push_stack( const char *filename, const char *fctname )\r\n", +"{\r\n", +" int16_t something;\r\n", +" int32_t current_stack_size;\r\n", +"\r\n", +" ptr_current_stack = &something;\r\n", +"\r\n", +" (void) *filename; /* to avoid compilation warning */\r\n", +"\r\n", +" if ( current_calls >= max_num_calls )\r\n", +" {\r\n", +" /* There is no room for a new record -> reallocate the list */\r\n", +" max_num_calls += MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" stack_callers[0] = realloc( stack_callers[0], max_num_calls * sizeof( caller_info ) );\r\n", +" stack_callers[1] = realloc( stack_callers[1], max_num_calls * sizeof( caller_info ) );\r\n", +" }\r\n", +"\r\n", +" /* Valid Function Name? */\r\n", +" if ( fctname[0] == 0 )\r\n", +" { /* No */\r\n", +" fprintf( stderr, \"Invalid function name for call stack info.\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* Save the Name of the Calling Function in the Table */\r\n", +" strncpy( stack_callers[0][current_calls].function_name, fctname, MAX_FUNCTION_NAME_LENGTH );\r\n", +" stack_callers[0][current_calls].function_name[MAX_FUNCTION_NAME_LENGTH] = 0; /* Nul Terminate */\r\n", +"\r\n", +" /* Save the Stack Pointer */\r\n", +" stack_callers[0][current_calls].stack_ptr = ptr_current_stack;\r\n", +"\r\n", +" /* Increase the Number of Calls in the List */\r\n", +" current_calls++;\r\n", +"\r\n", +" /* Is this the First Time or the Worst Case? */\r\n", +" if ( ptr_current_stack < ptr_max_stack || ptr_max_stack == NULL )\r\n", +" { /* Yes */\r\n", +" /* Save Info about it */\r\n", +" ptr_max_stack = ptr_current_stack;\r\n", +"\r\n", +" /* save the worst-case frame number */\r\n", +" /* current frame number is stored in the variable update_cnt and updated in the function update_wmops() */\r\n", +" wc_stack_frame = update_cnt; \r\n", +" strncpy( location_max_stack, fctname, sizeof( location_max_stack ) - 1 );\r\n", +" location_max_stack[sizeof( location_max_stack ) - 1] = '\\0';\r\n", +"\r\n", +" /* Save Call Tree */\r\n", +" memmove( stack_callers[1], stack_callers[0], sizeof( caller_info ) * current_calls );\r\n", +"\r\n", +" /* Terminate the List with 0 (for printing purposes) */\r\n", +" if ( current_calls < max_num_calls )\r\n", +" {\r\n", +" stack_callers[1][current_calls].function_name[0] = 0;\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* Check, if This is the New Worst-Case RAM (stack + heap) */\r\n", +" current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) );\r\n", +"\r\n", +" if ( current_stack_size < 0 )\r\n", +" {\r\n", +" /* prevent negative stack size */\r\n", +" current_stack_size = 0;\r\n", +" }\r\n", +"\r\n", +" if ( current_stack_size + current_heap_size > wc_ram_size )\r\n", +" {\r\n", +" wc_ram_size = current_stack_size + current_heap_size;\r\n", +" wc_ram_frame = update_cnt;\r\n", +" }\r\n", +"\r\n", +" return 0 /* for Now */;\r\n", +"}\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * pop_stack()\r\n", +" *\r\n", +" * Remove stack caller entry from the list\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"int pop_stack( const char *filename, const char *fctname )\r\n", +"{\r\n", +" caller_info *caller_info_ptr;\r\n", +"\r\n", +" (void) *filename; /* to avoid compilation warning */\r\n", +"\r\n", +" /* Decrease the Number of Records */\r\n", +" current_calls--;\r\n", +"\r\n", +" /* Get Pointer to Caller Information */\r\n", +" caller_info_ptr = &stack_callers[0][current_calls];\r\n", +"\r\n", +" /* Check, if the Function Names Match */\r\n", +" if ( strncmp( caller_info_ptr->function_name, fctname, MAX_FUNCTION_NAME_LENGTH ) != 0 )\r\n", +" {\r\n", +" fprintf( stderr, \"Invalid usage of pop_stack()\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* Erase Entry */\r\n", +" caller_info_ptr->function_name[0] = 0;\r\n", +"\r\n", +" /* Retrieve previous stack pointer */\r\n", +" if ( current_calls == 0 )\r\n", +" {\r\n", +" ptr_current_stack = ptr_base_stack;\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" ptr_current_stack = stack_callers[0][current_calls - 1].stack_ptr;\r\n", +" }\r\n", +"\r\n", +" return 0 /* for Now */;\r\n", +"}\r\n", +"\r\n", +"#ifdef MEM_COUNT_DETAILS\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * print_stack_call_tree()\r\n", +" *\r\n", +" * Print detailed information about worst-case stack usage\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"static void print_stack_call_tree( void )\r\n", +"{\r\n", +" caller_info *caller_info_ptr;\r\n", +" int call_level;\r\n", +" char fctname[MAX_FUNCTION_NAME_LENGTH + 1];\r\n", +"\r\n", +" fprintf( stdout, \"\\nList of functions when maximum stack size is reached:\\n\\n\" );\r\n", +"\r\n", +" caller_info_ptr = &stack_callers[1][0];\r\n", +" for ( call_level = 0; call_level < max_num_calls; call_level++ )\r\n", +" {\r\n", +" /* Done? */\r\n", +" if ( caller_info_ptr->function_name[0] == 0 )\r\n", +" {\r\n", +" break;\r\n", +" }\r\n", +"\r\n", +" /* Print Name */\r\n", +" strncpy( fctname, caller_info_ptr->function_name, MAX_FUNCTION_NAME_LENGTH );\r\n", +" strcat( fctname, \"()\" );\r\n", +" fprintf( stdout, \"%-42s\", fctname );\r\n", +"\r\n", +" /* Print Stack Usage (Based on Difference) */\r\n", +" if ( call_level != 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"%lu %s\\n\", ( ( ( caller_info_ptr - 1 )->stack_ptr - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" fprintf( stdout, \"%lu %s\\n\", ( ( ptr_base_stack - caller_info_ptr->stack_ptr ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] );\r\n", +" }\r\n", +"\r\n", +" /* Advance */\r\n", +" caller_info_ptr++;\r\n", +" }\r\n", +"\r\n", +" fprintf( stdout, \"\\n\" );\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"#endif\r\n", +"\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * mem_alloc()\r\n", +" *\r\n", +" * Creates new record, stores auxiliary information about which function allocated the memory, line number, parameters, etc.\r\n", +" * Finally, it allocates physical memory using malloc()\r\n", +" * The function also updates worst-case heap size and worst-case RAM size\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"void *mem_alloc(\r\n", +" const char *func_name,\r\n", +" int func_lineno,\r\n", +" size_t size,\r\n", +" char *size_str /* the first char indicates m-alloc or c-alloc */ )\r\n", +"{\r\n", +" int index_record;\r\n", +" int32_t current_stack_size;\r\n", +" unsigned long hash;\r\n", +" allocator_record *ptr_record;\r\n", +"\r\n", +" if ( size == 0 )\r\n", +" {\r\n", +" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Size of Zero not Supported\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* Search for an existing record (that has been de-allocated before) */\r\n", +" index_record = 0;\r\n", +" while ( ( ptr_record = get_mem_record( &hash, func_name, func_lineno, size_str, &index_record ) ) != NULL )\r\n", +" {\r\n", +" if ( ptr_record->frame_allocated == -1 )\r\n", +" {\r\n", +" break;\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" index_record++;\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* Create new record */\r\n", +" if ( ptr_record == NULL )\r\n", +" {\r\n", +" if ( Num_Records >= Max_Num_Records )\r\n", +" {\r\n", +" /* There is no room for a new record -> reallocate memory */\r\n", +" Max_Num_Records += MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" allocation_list = realloc( allocation_list, Max_Num_Records * sizeof( allocator_record ) );\r\n", +" }\r\n", +"\r\n", +" ptr_record = &( allocation_list[Num_Records] );\r\n", +"\r\n", +" /* Initialize new record */\r\n", +" ptr_record->hash = hash;\r\n", +" ptr_record->noccurances = 0;\r\n", +" ptr_record->total_block_size = 0;\r\n", +" ptr_record->total_used_size = 0;\r\n", +" ptr_record->frame_allocated = -1;\r\n", +" ptr_record->OOB_Flag = 0;\r\n", +" ptr_record->wc_heap_size_intra_frame = -1;\r\n", +" ptr_record->wc_heap_size_inter_frame = -1;\r\n", +"\r\n", +" index_record = Num_Records;\r\n", +" Num_Records++;\r\n", +" }\r\n", +"\r\n", +" /* Allocate memory block for the new record, add signature before the beginning and after the memory block and fill it with magic value */\r\n", +" ptr_record->block_ptr = mem_alloc_block( size, size_str );\r\n", +"\r\n", +" if ( ptr_record->block_ptr == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Error: Cannot Allocate Memory!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* Save all auxiliary information about the memory block */\r\n", +" strncpy( ptr_record->name, func_name, MAX_FUNCTION_NAME_LENGTH );\r\n", +" ptr_record->name[MAX_FUNCTION_NAME_LENGTH] = '\\0';\r\n", +" strncpy( ptr_record->params, size_str, MAX_PARAMS_LENGTH ); /* Note: The size string starts with either 'm' or 'c' to indicate 'm'alloc or 'c'alloc */\r\n", +" ptr_record->params[MAX_PARAMS_LENGTH] = '\\0';\r\n", +" ptr_record->lineno = func_lineno;\r\n", +" ptr_record->block_size = size;\r\n", +" ptr_record->total_block_size += size;\r\n", +"\r\n", +"#ifdef MEM_COUNT_DETAILS\r\n", +" /* Export heap memory allocation record to the .csv file */\r\n", +" fprintf( fid_csv_filename, \"A,%d,%s,%d,%d\\n\", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size );\r\n", +"#endif\r\n", +"\r\n", +" if ( ptr_record->frame_allocated != -1 )\r\n", +" {\r\n", +" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Error: Attempt to Allocate the Same Memory Block with Freeing it First!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" ptr_record->frame_allocated = update_cnt; /* Store the current frame number -> later it will be used to determine the total duration */\r\n", +"\r\n", +" /* Update Heap Size in the current frame */\r\n", +" current_heap_size += ptr_record->block_size;\r\n", +"\r\n", +" /* Check, if this is the new Worst-Case RAM (stack + heap) */\r\n", +" current_stack_size = (int32_t) ( ( ( ptr_base_stack - ptr_current_stack ) * sizeof( int16_t ) ) );\r\n", +" if ( current_stack_size + current_heap_size > wc_ram_size )\r\n", +" {\r\n", +" wc_ram_size = current_stack_size + current_heap_size;\r\n", +" wc_ram_frame = update_cnt;\r\n", +" }\r\n", +"\r\n", +" /* Add new entry to the heap allocation call tree */\r\n", +" if ( heap_allocation_call_tree == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Error: Heap allocation call tree not created!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* check, if the maximum size of the call tree has been reached -> resize if so */\r\n", +" if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size )\r\n", +" {\r\n", +" heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) );\r\n", +" }\r\n", +"\r\n", +" /* push new entry (positive number means push op, neagtive number means pop op; zero index must be converted to 0.01 :-) */\r\n", +" heap_allocation_call_tree[heap_allocation_call_tree_size++] = index_record;\r\n", +"\r\n", +" return ptr_record->block_ptr;\r\n", +"}\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * mem_alloc_block()\r\n", +" *\r\n", +" * Physical allocation of memory using malloc(). Appends 'signature' before and after the block,\r\n", +" * pre-fills memory block with magic value\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"static void *mem_alloc_block( size_t size, const char *size_str )\r\n", +"{\r\n", +" size_t rounded_size;\r\n", +" void *block_ptr;\r\n", +" char *tmp_ptr;\r\n", +" size_t n, f;\r\n", +" int32_t fill_value;\r\n", +" int32_t *ptr32;\r\n", +" int32_t mask, temp;\r\n", +"\r\n", +" /* Round Up Block Size */\r\n", +" rounded_size = ROUND_BLOCK_SIZE( size );\r\n", +"\r\n", +" /* Allocate memory using the standard malloc() by adding room for Signature Values */\r\n", +" block_ptr = malloc( rounded_size + BLOCK_ROUNDING * 2 );\r\n", +"\r\n", +" if ( block_ptr == NULL )\r\n", +" {\r\n", +" return NULL;\r\n", +" }\r\n", +"\r\n", +" /* Add Signature Before the Start of the Block */\r\n", +" ptr32 = (int32_t *) block_ptr;\r\n", +" n = N_32BITS_BLOCKS;\r\n", +" do\r\n", +" {\r\n", +" *ptr32++ = MAGIC_VALUE_OOB;\r\n", +" } while ( --n );\r\n", +"\r\n", +" /* Fill Memory Block with Magic Value or 0 */\r\n", +" fill_value = MAGIC_VALUE_USED;\r\n", +" if ( size_str[0] == 'c' )\r\n", +" {\r\n", +" fill_value = 0x00000000;\r\n", +" }\r\n", +" n = size / sizeof( int32_t );\r\n", +" while ( n-- )\r\n", +" {\r\n", +" *ptr32++ = fill_value;\r\n", +" }\r\n", +"\r\n", +" /* Fill the Reminder of the Memory Block - After Rounding */\r\n", +" n = rounded_size - size;\r\n", +" f = n % sizeof( int32_t );\r\n", +" if ( f != 0 )\r\n", +" {\r\n", +" /* when filling with '0' need to adapt the magic value */\r\n", +" /* shift by [1->24, 2->16, 3->8] */\r\n", +" mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 ); /* (1) */\r\n", +" temp = MAGIC_VALUE_OOB & mask;\r\n", +" if ( fill_value != 0x0 )\r\n", +" { /* for malloc merge fill value */\r\n", +" temp += ( ~mask ) & MAGIC_VALUE_USED;\r\n", +" } /* for calloc the code in (1) above already introduces zeros */\r\n", +" *ptr32++ = temp;\r\n", +" }\r\n", +" n /= sizeof( int32_t );\r\n", +" n += N_32BITS_BLOCKS;\r\n", +"\r\n", +" /* Add Signature After the End of Block */\r\n", +" do\r\n", +" {\r\n", +" *ptr32++ = MAGIC_VALUE_OOB;\r\n", +" } while ( --n );\r\n", +"\r\n", +" /* Adjust the Memory Block Pointer (Magic Value Before and After the Memory Block Requested) */\r\n", +" tmp_ptr = (char *) block_ptr;\r\n", +" tmp_ptr += BLOCK_ROUNDING;\r\n", +" block_ptr = (void *) tmp_ptr;\r\n", +"\r\n", +" return block_ptr;\r\n", +"}\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * mem_set_usage()\r\n", +" *\r\n", +" * Calculates actual usage of memory block by checking the magic value that was used to pre-fill\r\n", +" * each memory block during its allocation\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"static int mem_set_usage( allocator_record *record_ptr )\r\n", +"{\r\n", +" int total_bytes_used;\r\n", +"\r\n", +" size_t n;\r\n", +" int32_t *ptr32;\r\n", +" char *ptr8;\r\n", +" size_t total_bytes;\r\n", +" int32_t fill_value;\r\n", +"\r\n", +" fill_value = MAGIC_VALUE_USED;\r\n", +" if ( ( record_ptr->params[0] ) == 'c' )\r\n", +" {\r\n", +" fill_value = 0x00000000;\r\n", +" }\r\n", +"\r\n", +" total_bytes = record_ptr->block_size;\r\n", +"\r\n", +" /* Check 4 bytes at a time */\r\n", +" ptr32 = (int32_t *) record_ptr->block_ptr;\r\n", +" total_bytes_used = 0;\r\n", +" for ( n = total_bytes / sizeof( int32_t ); n > 0; n-- )\r\n", +" {\r\n", +" if ( *ptr32++ != fill_value )\r\n", +" {\r\n", +" total_bytes_used += sizeof( int32_t );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* Check remaining bytes (If Applicable) 1 byte at a time */\r\n", +" ptr8 = (char *) ptr32;\r\n", +" for ( n = total_bytes % sizeof( int32_t ); n > 0; n-- )\r\n", +" {\r\n", +" if ( *ptr8++ != (char) fill_value )\r\n", +" {\r\n", +" total_bytes_used++;\r\n", +" }\r\n", +"\r\n", +" /* Update Value */\r\n", +" fill_value >>= 8;\r\n", +" }\r\n", +"\r\n", +" return total_bytes_used;\r\n", +"}\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * mem_check_OOB()\r\n", +" *\r\n", +" * Checks, if out-of-bounds access has occured. This is done by inspecting the 'signature' value\r\n", +" * taht has been added before and after the memory block during its allocation\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"static unsigned int mem_check_OOB( allocator_record *record_ptr )\r\n", +"{\r\n", +" int32_t *ptr32;\r\n", +" unsigned int OOB_Flag = 0x0;\r\n", +" int32_t mask;\r\n", +" size_t i;\r\n", +" int f;\r\n", +"\r\n", +" ptr32 = (int32_t *) record_ptr->block_ptr - N_32BITS_BLOCKS;\r\n", +"\r\n", +" /* Check the Signature at the Beginning of Memory Block */\r\n", +" i = N_32BITS_BLOCKS;\r\n", +" do\r\n", +" {\r\n", +" if ( *ptr32++ ^ MAGIC_VALUE_OOB )\r\n", +" {\r\n", +" OOB_Flag |= OOB_START;\r\n", +" }\r\n", +" } while ( --i );\r\n", +"\r\n", +" /* Advance to End (Snap to lowest 32 Bits) */\r\n", +" ptr32 += record_ptr->block_size / sizeof( int32_t );\r\n", +"\r\n", +" /* Calculate Unused Space That has been added to get to the rounded Block Size */\r\n", +" i = ROUND_BLOCK_SIZE( record_ptr->block_size ) - record_ptr->block_size;\r\n", +"\r\n", +" /* Partial Check of Signature at the End of Memory Block (for block size that has been rounded) */\r\n", +" f = i % sizeof( int32_t );\r\n", +" if ( f != 0 )\r\n", +" {\r\n", +" mask = 0xFFFFFFFF << ( ( sizeof( int32_t ) - f ) * 8 );\r\n", +" if ( ( *ptr32++ ^ MAGIC_VALUE_OOB ) & mask )\r\n", +" {\r\n", +" OOB_Flag |= OOB_END;\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* Full Check of Signature at the End of Memory Block, i.e. all 32 Bits (for the remainder after rounding) */\r\n", +" i /= sizeof( int32_t );\r\n", +" i += N_32BITS_BLOCKS;\r\n", +" do\r\n", +" {\r\n", +" if ( *ptr32++ ^ MAGIC_VALUE_OOB )\r\n", +" {\r\n", +" OOB_Flag |= OOB_END;\r\n", +" }\r\n", +" } while ( --i );\r\n", +"\r\n", +" return OOB_Flag;\r\n", +"}\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * malloc_hash()\r\n", +" *\r\n", +" * Calculate hash from function name, line number and malloc size\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"static unsigned long malloc_hash( const char *func_name, int func_lineno, char *size_str )\r\n", +"{\r\n", +" unsigned long hash = 5381;\r\n", +" const char *ptr_str;\r\n", +"\r\n", +" ptr_str = func_name;\r\n", +" while ( ptr_str != NULL && *ptr_str != '\\0' )\r\n", +" {\r\n", +" hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */\r\n", +" }\r\n", +"\r\n", +" hash = ( ( hash << 5 ) + hash ) + func_lineno; /* hash * 33 + func_lineno */\r\n", +"\r\n", +" ptr_str = size_str;\r\n", +" while ( ptr_str != NULL && *ptr_str != '\\0' )\r\n", +" {\r\n", +" hash = ( ( hash << 5 ) + hash ) + *ptr_str++; /* hash * 33 + char */\r\n", +" }\r\n", +"\r\n", +" return hash;\r\n", +"}\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * get_mem_record()\r\n", +" *\r\n", +" * Search for memory record in the internal list, return NULL if not found\r\n", +" * Start from index_record\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"allocator_record *get_mem_record( unsigned long *hash, const char *func_name, int func_lineno, char *size_str, int *index_record )\r\n", +"{\r\n", +" int i;\r\n", +"\r\n", +" if ( *index_record < 0 || *index_record > Num_Records )\r\n", +" {\r\n", +" return NULL;\r\n", +" }\r\n", +"\r\n", +" /* calculate hash */\r\n", +" *hash = malloc_hash( func_name, func_lineno, size_str );\r\n", +"\r\n", +" for ( i = *index_record; i < Num_Records; i++ )\r\n", +" {\r\n", +" /* check, if memory block is not allocated at the moment and the hash matches */\r\n", +" if ( allocation_list[i].block_ptr == NULL && *hash == allocation_list[i].hash )\r\n", +" {\r\n", +" *index_record = i;\r\n", +" return &( allocation_list[i] );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* not found */\r\n", +" *index_record = -1;\r\n", +" return NULL;\r\n", +"}\r\n", +"\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * mem_free()\r\n", +" *\r\n", +" * This function de-allocatesd the memory block and frees the mphysical memory with free().\r\n", +" * It also updates actual and average usage of the memory block.\r\n", +" *\r\n", +" * Note: The record is not removed from the list and may be reused later on in mem_alloc()!\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"void mem_free( const char *func_name, int func_lineno, void *ptr )\r\n", +"{\r\n", +" int i, index_record;\r\n", +" char *tmp_ptr;\r\n", +" allocator_record *ptr_record;\r\n", +"\r\n", +" /* Search for the Block Pointer in the List */\r\n", +" ptr_record = NULL;\r\n", +" index_record = -1;\r\n", +" for ( i = 0; i < Num_Records; i++ )\r\n", +" {\r\n", +" if ( ptr == allocation_list[i].block_ptr )\r\n", +" { /* Yes, Found it */\r\n", +" ptr_record = &( allocation_list[i] );\r\n", +" index_record = i;\r\n", +" break;\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" if ( ptr_record == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", func_name, func_lineno, \"Error: Unable to Find Record Corresponding to the Allocated Memory Block!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* Update the Heap Size */\r\n", +" current_heap_size -= ptr_record->block_size;\r\n", +"\r\n", +" /* Calculate the Actual Usage of the Memory Block (Look for Signature) */\r\n", +" ptr_record->total_used_size += mem_set_usage( ptr_record );\r\n", +"\r\n", +" /* Check, if Out-Of-Bounds Access has been Detected */\r\n", +" ptr_record->OOB_Flag = mem_check_OOB( ptr_record );\r\n", +"\r\n", +"#ifdef MEM_COUNT_DETAILS\r\n", +" /* Export heap memory de-allocation record to the .csv file */\r\n", +" fprintf( fid_csv_filename, \"D,%d,%s,%d,%d\\n\", update_cnt, ptr_record->name, ptr_record->lineno, ptr_record->block_size );\r\n", +"#endif\r\n", +"\r\n", +" /* De-Allocate Memory Block */\r\n", +" tmp_ptr = (char *) ptr;\r\n", +" tmp_ptr -= BLOCK_ROUNDING;\r\n", +" ptr = (void *) tmp_ptr;\r\n", +" free( ptr );\r\n", +"\r\n", +" /* Add new entry to the heap allocation call tree */\r\n", +" if ( heap_allocation_call_tree == NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Error: Heap allocation call tree not created!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +"\r\n", +" /* check, if the maximum size of the call tree has been reached -> resize if so */\r\n", +" if ( heap_allocation_call_tree_size >= heap_allocation_call_tree_max_size )\r\n", +" {\r\n", +" heap_allocation_call_tree_max_size += MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" heap_allocation_call_tree = (int *) realloc( heap_allocation_call_tree, heap_allocation_call_tree_max_size * sizeof( int ) );\r\n", +" }\r\n", +"\r\n", +" heap_allocation_call_tree[heap_allocation_call_tree_size++] = -index_record;\r\n", +"\r\n", +" /* Reset memory block pointer (this is checked when updating wc intra-frame and inter-frame memory) */\r\n", +" ptr_record->block_ptr = NULL;\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * update_mem()\r\n", +" *\r\n", +" * This function updates the worst-case intra-frame memory and the worst-case inter-frame memory.\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"void update_mem( void )\r\n", +"{\r\n", +" int i, j, flag_alloc = -1, i_record;\r\n", +" int size_current_intra_frame_heap;\r\n", +" int *list_current_intra_frame_heap = NULL, n_items_current_intra_frame_heap;\r\n", +" allocator_record *ptr_record;\r\n", +"\r\n", +" /* process the heap allocation call tree and prepare lists of intra-frame and inter-frame heap memory blocks for this frame */\r\n", +" n_items_current_intra_frame_heap = 0;\r\n", +" size_current_intra_frame_heap = 0;\r\n", +" for ( i = 0; i < heap_allocation_call_tree_size; i++ )\r\n", +" {\r\n", +" /* get the record */\r\n", +" i_record = heap_allocation_call_tree[i];\r\n", +"\r\n", +" if ( i_record > 0 )\r\n", +" {\r\n", +" flag_alloc = 1;\r\n", +" }\r\n", +" else if ( i_record < 0 )\r\n", +" {\r\n", +" flag_alloc = 0;\r\n", +" i_record = -i_record;\r\n", +" }\r\n", +" ptr_record = &( allocation_list[i_record] );\r\n", +"\r\n", +" if ( ptr_record->frame_allocated == update_cnt && ptr_record->block_ptr == NULL )\r\n", +" {\r\n", +" /* intra-frame heap memory */\r\n", +" if ( list_current_intra_frame_heap == NULL )\r\n", +" {\r\n", +" list_current_intra_frame_heap = (int *) malloc( heap_allocation_call_tree_size * sizeof( int ) );\r\n", +" memset( list_current_intra_frame_heap, -1, heap_allocation_call_tree_size * sizeof( int ) );\r\n", +" }\r\n", +"\r\n", +" /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */\r\n", +" if ( i_record == 0 )\r\n", +" {\r\n", +" flag_alloc = 1;\r\n", +" for ( j = 0; j < n_items_current_intra_frame_heap; j++ )\r\n", +" {\r\n", +" if ( list_current_intra_frame_heap[j] == i_record )\r\n", +" {\r\n", +" flag_alloc = 0;\r\n", +" break;\r\n", +" }\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" if ( flag_alloc )\r\n", +" {\r\n", +" /* add to list */\r\n", +" list_current_intra_frame_heap[n_items_current_intra_frame_heap++] = i_record;\r\n", +" size_current_intra_frame_heap += ptr_record->block_size;\r\n", +"\r\n", +" /* no need to re-size the list -> the initially allocated size should be large enough */\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" /* remove from list */\r\n", +" for ( j = 0; j < n_items_current_intra_frame_heap; j++ )\r\n", +" {\r\n", +" if ( list_current_intra_frame_heap[j] == i_record )\r\n", +" {\r\n", +" break;\r\n", +" }\r\n", +" }\r\n", +" memmove( &list_current_intra_frame_heap[j], &list_current_intra_frame_heap[j + 1], ( n_items_current_intra_frame_heap - j ) * sizeof( int ) );\r\n", +" n_items_current_intra_frame_heap--;\r\n", +" size_current_intra_frame_heap -= ptr_record->block_size;\r\n", +"\r\n", +" /* reset block size */\r\n", +" ptr_record->frame_allocated = -1;\r\n", +" ptr_record->block_size = 0;\r\n", +" }\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" /* inter-frame heap memory */\r\n", +"\r\n", +" /* zero index doesn't have sign to determine whether it's allocated or de-allocated -> we need to search the list */\r\n", +" if ( i_record == 0 )\r\n", +" {\r\n", +" flag_alloc = 1;\r\n", +" for ( j = 0; j < n_items_current_inter_frame_heap; j++ )\r\n", +" {\r\n", +" if ( list_current_inter_frame_heap[j] == i_record )\r\n", +" {\r\n", +" flag_alloc = 0;\r\n", +" break;\r\n", +" }\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" if ( flag_alloc )\r\n", +" {\r\n", +" /* add to list */\r\n", +" if ( n_items_current_inter_frame_heap >= max_items_current_inter_frame_heap )\r\n", +" {\r\n", +" /* resize list, if needed */\r\n", +" max_items_current_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" list_current_inter_frame_heap = realloc( list_current_inter_frame_heap, max_items_current_inter_frame_heap * sizeof( int ) );\r\n", +" }\r\n", +"\r\n", +" list_current_inter_frame_heap[n_items_current_inter_frame_heap++] = i_record;\r\n", +" size_current_inter_frame_heap += ptr_record->block_size;\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" /* remove from list */\r\n", +" for ( j = 0; j < n_items_current_inter_frame_heap; j++ )\r\n", +" {\r\n", +" if ( list_current_inter_frame_heap[j] == i_record )\r\n", +" {\r\n", +" break;\r\n", +" }\r\n", +" }\r\n", +" memmove( &list_current_inter_frame_heap[j], &list_current_inter_frame_heap[j + 1], ( n_items_current_inter_frame_heap - j ) * sizeof( int ) );\r\n", +" n_items_current_inter_frame_heap--;\r\n", +" size_current_inter_frame_heap -= ptr_record->block_size;\r\n", +"\r\n", +" /* reset block size */\r\n", +" ptr_record->frame_allocated = -1;\r\n", +" ptr_record->block_size = 0;\r\n", +" }\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* check, if this is the new worst-case for intra-frame heap memory */\r\n", +" if ( size_current_intra_frame_heap > size_wc_intra_frame_heap )\r\n", +" {\r\n", +" if ( n_items_current_intra_frame_heap >= max_items_wc_intra_frame_heap )\r\n", +" {\r\n", +" /* resize the list, if needed */\r\n", +" max_items_wc_intra_frame_heap = n_items_current_intra_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" list_wc_intra_frame_heap = realloc( list_wc_intra_frame_heap, max_items_wc_intra_frame_heap * sizeof( int ) );\r\n", +" }\r\n", +"\r\n", +" /* copy current-frame list to worst-case list */\r\n", +" memmove( list_wc_intra_frame_heap, list_current_intra_frame_heap, n_items_current_intra_frame_heap * sizeof( int ) );\r\n", +" n_items_wc_intra_frame_heap = n_items_current_intra_frame_heap;\r\n", +" size_wc_intra_frame_heap = size_current_intra_frame_heap;\r\n", +" location_wc_intra_frame_heap = update_cnt;\r\n", +"\r\n", +" /* update the wc numbers in all individual records */\r\n", +" for ( i = 0; i < n_items_wc_intra_frame_heap; i++ )\r\n", +" {\r\n", +" i_record = list_wc_intra_frame_heap[i];\r\n", +" ptr_record = &( allocation_list[i_record] );\r\n", +" ptr_record->wc_heap_size_intra_frame = ptr_record->block_size;\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* check, if this is the new worst-case for inter-frame heap memory */\r\n", +" if ( size_current_inter_frame_heap > size_wc_inter_frame_heap )\r\n", +" {\r\n", +" if ( n_items_current_inter_frame_heap >= max_items_wc_inter_frame_heap )\r\n", +" {\r\n", +" /* resize list, if needed */\r\n", +" max_items_wc_inter_frame_heap = n_items_current_inter_frame_heap + MAX_NUM_RECORDS_REALLOC_STEP;\r\n", +" list_wc_inter_frame_heap = realloc( list_wc_inter_frame_heap, max_items_wc_inter_frame_heap * sizeof( int ) );\r\n", +" }\r\n", +"\r\n", +" /* copy current-frame list to worst-case list */\r\n", +" memmove( list_wc_inter_frame_heap, list_current_inter_frame_heap, n_items_current_inter_frame_heap * sizeof( int ) );\r\n", +" n_items_wc_inter_frame_heap = n_items_current_inter_frame_heap;\r\n", +" size_wc_inter_frame_heap = size_current_inter_frame_heap;\r\n", +" location_wc_inter_frame_heap = update_cnt;\r\n", +"\r\n", +" /* update the wc numbers in all individual records */\r\n", +" for ( i = 0; i < n_items_wc_inter_frame_heap; i++ )\r\n", +" {\r\n", +" i_record = list_wc_inter_frame_heap[i];\r\n", +" ptr_record = &( allocation_list[i_record] );\r\n", +" ptr_record->wc_heap_size_inter_frame = ptr_record->block_size;\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* reset heap allocation call tree */\r\n", +" heap_allocation_call_tree_size = 0;\r\n", +"\r\n", +" /* de-allocate list of intra-frame heap memory blocks in the current fraeme - it's needed only inside this function */\r\n", +" if ( list_current_intra_frame_heap )\r\n", +" {\r\n", +" free( list_current_intra_frame_heap );\r\n", +" }\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"#ifdef MEM_COUNT_DETAILS\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * subst()\r\n", +" *\r\n", +" * Substitute character in string\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"static void subst( char *s, char from, char to )\r\n", +"{\r\n", +" while ( *s == from )\r\n", +" {\r\n", +" *s++ = to;\r\n", +" }\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * mem_count_summary()\r\n", +" *\r\n", +" * Print detailed (per-item) information about heap memory usage\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"static void mem_count_summary( void )\r\n", +"{\r\n", +" int i, j, index, index_record;\r\n", +" size_t length;\r\n", +" char buf[300], format_str[50], name_str[MAX_FUNCTION_NAME_LENGTH + 3], parms_str[MAX_PARAMS_LENGTH + 1], type_str[10], usage_str[20], size_str[20], line_str[10];\r\n", +" allocator_record *ptr_record, *ptr;\r\n", +"\r\n", +" /* Prepare format string */\r\n", +" sprintf( format_str, \"%%-%ds %%5s %%6s %%-%ds %%20s %%6s \", MAX_FUNCTION_NAME_LENGTH, MAX_PARAMS_LENGTH );\r\n", +"\r\n", +" if ( n_items_wc_intra_frame_heap > 0 )\r\n", +" {\r\n", +" /* Intra-Frame Heap Size */\r\n", +" fprintf( stdout, \"\\nList of memory blocks when maximum intra-frame heap size is reached:\\n\\n\" );\r\n", +"\r\n", +" /* Find duplicate records (same hash and worst-case heap size) */\r\n", +" for ( i = 0; i < n_items_wc_intra_frame_heap; i++ )\r\n", +" {\r\n", +" index_record = list_wc_intra_frame_heap[i];\r\n", +" if ( index_record == -1 )\r\n", +" {\r\n", +" continue;\r\n", +" }\r\n", +"\r\n", +" ptr_record = &( allocation_list[index_record] );\r\n", +" for ( j = i + 1; j < n_items_wc_intra_frame_heap; j++ )\r\n", +" {\r\n", +" index = list_wc_intra_frame_heap[j];\r\n", +" if ( index == -1 )\r\n", +" {\r\n", +" continue;\r\n", +" }\r\n", +" ptr = &( allocation_list[index] );\r\n", +"\r\n", +" if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_intra_frame == ptr_record->wc_heap_size_intra_frame )\r\n", +" {\r\n", +" ptr_record->noccurances++;\r\n", +" list_wc_intra_frame_heap[j] = -1;\r\n", +" }\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* Print Header */\r\n", +" sprintf( buf, format_str, \"Function Name\", \"Line\", \"Type\", \"Function Parameters\", \"Maximum Size\", \"Usage\" );\r\n", +" puts( buf );\r\n", +" length = strlen( buf );\r\n", +" sprintf( buf, \"%0*d\\n\", (int) length - 1, 0 );\r\n", +" subst( buf, '0', '-' );\r\n", +" puts( buf );\r\n", +"\r\n", +" for ( i = 0; i < n_items_wc_intra_frame_heap; i++ )\r\n", +" {\r\n", +" index_record = list_wc_intra_frame_heap[i];\r\n", +"\r\n", +" if ( index_record != -1 )\r\n", +" {\r\n", +" /* get the record */\r\n", +" ptr_record = &( allocation_list[index_record] );\r\n", +"\r\n", +" /* prepare information strings */\r\n", +" strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH );\r\n", +" strcat( name_str, \"()\" );\r\n", +" name_str[MAX_FUNCTION_NAME_LENGTH] = '\\0';\r\n", +" strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH );\r\n", +" parms_str[MAX_PARAMS_LENGTH] = '\\0';\r\n", +"\r\n", +" if ( ptr_record->params[0] == 'm' )\r\n", +" {\r\n", +" strcpy( type_str, \"malloc\" );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" strcpy( type_str, \"calloc\" );\r\n", +" }\r\n", +"\r\n", +" sprintf( line_str, \"%d\", ptr_record->lineno );\r\n", +"\r\n", +" /* prepare average usage & memory size strings */\r\n", +" sprintf( usage_str, \"%d%%\", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 1 ) ) * 100.0f ) );\r\n", +"\r\n", +" if ( ptr_record->noccurances > 1 )\r\n", +" {\r\n", +" sprintf( size_str, \"%dx%d %s\", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" sprintf( size_str, \"%d %s\", (int) ( ptr_record->wc_heap_size_intra_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n", +" }\r\n", +"\r\n", +" sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str );\r\n", +" puts( buf );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" fprintf( stdout, \"\\n\" );\r\n", +" }\r\n", +"\r\n", +" if ( n_items_wc_inter_frame_heap > 0 )\r\n", +" {\r\n", +" /* Inter-Frame Heap Size */\r\n", +" fprintf( stdout, \"\\nList of memory blocks when maximum inter-frame heap size is reached:\\n\\n\" );\r\n", +"\r\n", +" /* Find duplicate records (same hash and worst-case heap size) */\r\n", +" for ( i = 0; i < n_items_wc_inter_frame_heap; i++ )\r\n", +" {\r\n", +" index_record = list_wc_inter_frame_heap[i];\r\n", +" if ( index_record == -1 )\r\n", +" {\r\n", +" continue;\r\n", +" }\r\n", +" ptr_record = &( allocation_list[index_record] );\r\n", +" ptr_record->noccurances = 1; /* reset the counter as some blocks may have been both, intra-frame and inter-frame */\r\n", +" for ( j = i + 1; j < n_items_wc_inter_frame_heap; j++ )\r\n", +" {\r\n", +" index = list_wc_inter_frame_heap[j];\r\n", +" if ( index == -1 )\r\n", +" {\r\n", +" continue;\r\n", +" }\r\n", +" ptr = &( allocation_list[index] );\r\n", +"\r\n", +" if ( ptr->hash == ptr_record->hash && ptr->wc_heap_size_inter_frame == ptr_record->wc_heap_size_inter_frame )\r\n", +" {\r\n", +" ptr_record->noccurances++;\r\n", +" list_wc_inter_frame_heap[j] = -1;\r\n", +" }\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" /* Print Header */\r\n", +" sprintf( buf, format_str, \"Function Name\", \"Line\", \"Type\", \"Function Parameters\", \"Memory Size\", \"Usage\" );\r\n", +" puts( buf );\r\n", +" length = strlen( buf );\r\n", +" sprintf( buf, \"%0*d\\n\", (int) length - 1, 0 );\r\n", +" subst( buf, '0', '-' );\r\n", +" puts( buf );\r\n", +"\r\n", +" for ( i = 0; i < n_items_wc_inter_frame_heap; i++ )\r\n", +" {\r\n", +" index_record = list_wc_inter_frame_heap[i];\r\n", +"\r\n", +" if ( index_record != -1 )\r\n", +" {\r\n", +" /* get the record */\r\n", +" ptr_record = &( allocation_list[index_record] );\r\n", +"\r\n", +" /* prepare information strings */\r\n", +" strncpy( name_str, ptr_record->name, MAX_FUNCTION_NAME_LENGTH );\r\n", +" strcat( name_str, \"()\" );\r\n", +" name_str[MAX_FUNCTION_NAME_LENGTH] = '\\0';\r\n", +" strncpy( parms_str, &( ptr_record->params[2] ), MAX_PARAMS_LENGTH );\r\n", +" parms_str[MAX_PARAMS_LENGTH] = '\\0';\r\n", +"\r\n", +" if ( ptr_record->params[0] == 'm' )\r\n", +" {\r\n", +" strcpy( type_str, \"malloc\" );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" strcpy( type_str, \"calloc\" );\r\n", +" }\r\n", +"\r\n", +" sprintf( line_str, \"%d\", ptr_record->lineno );\r\n", +"\r\n", +" /* prepare average usage & memory size strings */\r\n", +" sprintf( usage_str, \"%d%%\", (int) ( ( (float) ptr_record->total_used_size / ( ptr_record->total_block_size + 0.1f ) ) * 100.0f + 0.5f ) );\r\n", +"\r\n", +" if ( ptr_record->noccurances > 1 )\r\n", +" {\r\n", +" sprintf( size_str, \"%dx%d %s\", ptr_record->noccurances, (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" sprintf( size_str, \"%d %s\", (int) ( ptr_record->wc_heap_size_inter_frame >> Stat_Cnt_Size ), Count_Unit[Stat_Cnt_Size] );\r\n", +" }\r\n", +"\r\n", +" sprintf( buf, format_str, name_str, line_str, type_str, parms_str, size_str, usage_str );\r\n", +" puts( buf );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" fprintf( stdout, \"\\n\" );\r\n", +" }\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"#endif\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * print_mem()\r\n", +" *\r\n", +" * Print information about ROM and RAM memory usage\r\n", +" *--------------------------------------------------------------------*/\r\n", +"\r\n", +"void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] )\r\n", +"{\r\n", +" int i, nElem;\r\n", +"\r\n", +" fprintf( stdout, \"\\n\\n --- Memory usage --- \\n\\n\" );\r\n", +"\r\n", +" if ( Const_Data_PROM_Table != NULL )\r\n", +" {\r\n", +" nElem = 0;\r\n", +" while ( strcmp( Const_Data_PROM_Table[nElem].file_spec, \"\" ) != 0 )\r\n", +" nElem++;\r\n", +"\r\n", +" for ( i = 0; i < nElem; i++ )\r\n", +" {\r\n", +" if ( Stat_Cnt_Size > 0 )\r\n", +" {\r\n", +" /* words */\r\n", +" fprintf( stdout, \"Program ROM size (%s): %d words\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" /* bytes (here, we assume that each instruction takes PROM_INST_SIZE bits of the PROM memory) */\r\n", +" fprintf( stdout, \"Program ROM size (%s): %d bytes\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].PROM_size * ( PROM_INST_SIZE / 8 ) );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" for ( i = 0; i < nElem; i++ )\r\n", +" {\r\n", +" if ( Const_Data_PROM_Table[i].Get_Const_Data_Size_Func == NULL )\r\n", +" {\r\n", +" fprintf( stdout, \"Error: Cannot retrieve or calculate Table ROM size of (%s)!\\n\", Const_Data_PROM_Table[i].file_spec );\r\n", +" }\r\n", +"\r\n", +" fprintf( stdout, \"Table ROM (const data) size (%s): %d %s\\n\", Const_Data_PROM_Table[i].file_spec, Const_Data_PROM_Table[i].Get_Const_Data_Size_Func() >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size] );\r\n", +" }\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" fprintf( stdout, \"Program ROM size: not available\\n\" );\r\n", +" fprintf( stdout, \"Table ROM (const data) size: not available\\n\" );\r\n", +" }\r\n", +"\r\n", +" if ( wc_ram_size > 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"Maximum RAM (stack + heap) size: %d %s in frame %d\\n\", wc_ram_size >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], wc_ram_frame );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" fprintf( stdout, \"Maximum RAM (stack + heap) size: not available\\n\" );\r\n", +" }\r\n", +"\r\n", +" /* check, if the stack is empty */\r\n", +" if ( ptr_current_stack != ptr_base_stack )\r\n", +" {\r\n", +" fprintf( stderr, \"Warning: Stack is not empty.\\n\" );\r\n", +" }\r\n", +"\r\n", +" if ( ptr_base_stack - ptr_max_stack > 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"Maximum stack size: %lu %s in frame %d\\n\", ( ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) ) >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size],\r\n", +" wc_stack_frame );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" fprintf( stdout, \"Maximum stack size: not available\\n\" );\r\n", +" }\r\n", +"\r\n", +" /* last update of intra-frame memory and inter-frame memory, if needed */\r\n", +" if ( heap_allocation_call_tree_size > 0 )\r\n", +" {\r\n", +" update_mem();\r\n", +" }\r\n", +"\r\n", +" /* check, if all memory blocks have been deallocated (freed) */\r\n", +" for ( i = 0; i < Num_Records; i++ )\r\n", +" {\r\n", +" if ( allocation_list[i].block_ptr != NULL )\r\n", +" {\r\n", +" fprintf( stderr, \"Fct=%s, Ln=%i: %s!\\n\", allocation_list[i].name, allocation_list[i].lineno, \"Error: Memory Block has not been De-Allocated with free()!\" );\r\n", +" exit( -1 );\r\n", +" }\r\n", +" }\r\n", +"\r\n", +" if ( n_items_wc_intra_frame_heap > 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"Maximum intra-frame heap size: %d %s in frame %d\\n\", size_wc_intra_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_intra_frame_heap );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" fprintf( stdout, \"Maximum intra-frame heap size: 0\\n\" );\r\n", +" }\r\n", +"\r\n", +" if ( n_items_wc_inter_frame_heap > 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"Maximum inter-frame heap size: %d %s in frame %d\\n\", size_wc_inter_frame_heap >> Stat_Cnt_Size, Count_Unit[Stat_Cnt_Size], location_wc_inter_frame_heap );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" fprintf( stdout, \"Maximum inter-frame heap size: 0\\n\" );\r\n", +" }\r\n", +"\r\n", +"#ifdef MEM_COUNT_DETAILS\r\n", +" /* Print detailed information about worst-case stack usage */\r\n", +" if ( ptr_base_stack - ptr_max_stack > 0 )\r\n", +" {\r\n", +" print_stack_call_tree();\r\n", +" }\r\n", +"\r\n", +" /* Print detailed information about worst-case heap usage */\r\n", +" mem_count_summary();\r\n", +"#endif\r\n", +"\r\n", +" if ( Stat_Cnt_Size > 0 )\r\n", +" {\r\n", +" /* words */\r\n", +" fprintf( stdout, \"\\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bits\\n\", 8 << Stat_Cnt_Size );\r\n", +" }\r\n", +" else\r\n", +" {\r\n", +" /* bytes */\r\n", +" fprintf( stdout, \"\\nNote: The Program ROM size is calculated under the assumption that 1 instruction word is stored with %d bits\\n\", PROM_INST_SIZE );\r\n", +" }\r\n", +" fprintf( stdout, \"Note: The Data ROM size is calculated using the sizeof(type) built-in function\\n\" );\r\n", +"\r\n", +" if ( n_items_wc_intra_frame_heap > 0 )\r\n", +" {\r\n", +" fprintf( stdout, \"Intra-frame heap memory is allocated and de-allocated in the same frame\\n\" );\r\n", +" }\r\n", +"\r\n", +" /* De-allocate list of heap memory blocks */\r\n", +" if ( allocation_list != NULL )\r\n", +" {\r\n", +" free( allocation_list );\r\n", +" }\r\n", +"\r\n", +" /* De-allocate list of stack records */\r\n", +" if ( stack_callers[0] != NULL )\r\n", +" {\r\n", +" free( stack_callers[0] );\r\n", +" }\r\n", +"\r\n", +" if ( stack_callers[1] != NULL )\r\n", +" {\r\n", +" free( stack_callers[1] );\r\n", +" }\r\n", +"\r\n", +" /* De-allocate heap allocation call tree */\r\n", +" if ( heap_allocation_call_tree != NULL )\r\n", +" {\r\n", +" free( heap_allocation_call_tree );\r\n", +" }\r\n", +"\r\n", +" /* De-allocate intra-frame and inter-frame heap lists */\r\n", +" if ( list_wc_intra_frame_heap != NULL )\r\n", +" {\r\n", +" free( list_wc_intra_frame_heap );\r\n", +" }\r\n", +"\r\n", +" if ( list_current_inter_frame_heap != NULL )\r\n", +" {\r\n", +" free( list_current_inter_frame_heap );\r\n", +" }\r\n", +"\r\n", +" if ( list_wc_inter_frame_heap != NULL )\r\n", +" {\r\n", +" free( list_wc_inter_frame_heap );\r\n", +" }\r\n", +"\r\n", +"#ifdef MEM_COUNT_DETAILS\r\n", +" if ( fid_csv_filename != NULL )\r\n", +" {\r\n", +" fclose( fid_csv_filename );\r\n", +" }\r\n", +"#endif\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"#endif /* WMOPS */\r\n", +"\r\n", +"#ifndef WMOPS\r\n", +"int cntr_push_pop = 0; /* global counter for checking balanced push_wmops()/pop_wmops() pairs when WMOPS is not activated */\r\n", +"#endif\r\n", +"\r\n", +"#ifdef WMOPS\r\n", +"/* Global counter for the calculation of BASOP complexity */\r\n", +"BASIC_OP *multiCounter = NULL;\r\n", +"int currCounter = 0;\r\n", +"int funcId_where_last_call_to_else_occurred;\r\n", +"long funcid_total_wmops_at_last_call_to_else;\r\n", +"int call_occurred = 1;\r\n", +"\r\n", +"BASIC_OP op_weight = {\r\n", +" 1, 1, 1, 1, 1,\r\n", +" 1, 1, 1, 1, 1,\r\n", +" 1, 1, 1, 1, 1,\r\n", +" 1, 1, 2, 2, 1,\r\n", +" 1, 1, 1, 3, 1,\r\n", +"\r\n", +" 1, 1, 1, 3, 1,\r\n", +" 4, 1, 18, 1, 1,\r\n", +" 2, 1, 2, 2, 1,\r\n", +" 1, 1, 1, 1, 1,\r\n", +" 3, 3, 3, 3, 1,\r\n", +"\r\n", +" 1, 1, 1, 1, 1,\r\n", +" 1, 1, 1, 2,\r\n", +" 1, 2, 2, 4, 1,\r\n", +" 1, 1, 1, 1, 1,\r\n", +" 1, 1, 1, 1, 1,\r\n", +"\r\n", +" 1, 1, 1, 1, 3,\r\n", +" 3, 3, 3, 3, 1,\r\n", +" 1, 1, 1, 1, 1,\r\n", +" 1, 1, 1, 4, 4,\r\n", +" 4, 8, 3, 4, 4,\r\n", +"\r\n", +" 5, 32, 3\r\n", +"};\r\n", +"\r\n", +"/* Set the counter group to use, default is zero */\r\n", +"void Set_BASOP_WMOPS_counter( int counterId )\r\n", +"{\r\n", +" if ( ( counterId > num_wmops_records ) || ( counterId < 0 ) )\r\n", +" {\r\n", +" currCounter = 0;\r\n", +" return;\r\n", +" }\r\n", +" currCounter = counterId;\r\n", +" call_occurred = 1;\r\n", +"}\r\n", +"\r\n", +"extern int32_t frame;\r\n", +"\r\n", +"long TotalWeightedOperation()\r\n", +"{\r\n", +" int i;\r\n", +" unsigned int *ptr, *ptr2;\r\n", +" long tot; \r\n", +"\r\n", +" tot = 0;\r\n", +" ptr = (unsigned int *) &multiCounter[currCounter];\r\n", +" ptr2 = (unsigned int *) &op_weight;\r\n", +"\r\n", +" for ( i = 0; i < ( int )( sizeof( multiCounter[currCounter] ) / sizeof( unsigned int ) ); i++ )\r\n", +" {\r\n", +" tot += ( ( *ptr++ ) * ( *ptr2++ ) );\r\n", +" }\r\n", +"\r\n", +" return ( tot );\r\n", +"}\r\n", +"\r\n", +"long DeltaWeightedOperation( void )\r\n", +"{\r\n", +" long NewWOper, delta;\r\n", +"\r\n", +" NewWOper = TotalWeightedOperation();\r\n", +"\r\n", +" delta = NewWOper - wmops[currCounter].LastWOper;\r\n", +" wmops[currCounter].LastWOper = NewWOper;\r\n", +"\r\n", +" return ( delta );\r\n", +"}\r\n", +"\r\n", +"/* Resets the current BASOP WMOPS counter */\r\n", +"void Reset_BASOP_WMOPS_counter( void )\r\n", +"{\r\n", +" int i;\r\n", +" long *ptr;\r\n", +"\r\n", +" /* clear the current BASOP operation counter before new frame begins */\r\n", +" ptr = (long *) &multiCounter[currCounter];\r\n", +" for ( i = 0; i < (int) ( sizeof( multiCounter[currCounter] ) / sizeof( long ) ); i++ )\r\n", +" {\r\n", +" *ptr++ = 0;\r\n", +" }\r\n", +"\r\n", +" wmops[currCounter].LastWOper = 0;\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"#endif\r\n", +"\r\n", +"\r\n", +"\r\n", \ No newline at end of file diff --git a/src/wmc_tool/wmc_auto_h.txt b/src/wmc_tool/wmc_auto_h.txt index e8d3c332..caed2709 100644 --- a/src/wmc_tool/wmc_auto_h.txt +++ b/src/wmc_tool/wmc_auto_h.txt @@ -1,1448 +1,1448 @@ -"/*\r\n" -" * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved.\r\n" -" *\r\n" -" * This software is protected by copyright law and by international treaties. The source code, and all of its derivations,\r\n" -" * is provided by VoiceAge Corporation under the \"ITU-T Software Tools' General Public License\". Please, read the license file\r\n" -" * or refer to ITU-T Recommendation G.191 on \"SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS\".\r\n" -" *\r\n" -" * Any use of this software is permitted provided that this notice is not removed and that neither the authors nor\r\n" -" * VoiceAge Corporation are deemed to have made any representations as to the suitability of this software\r\n" -" * for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE.\r\n" -" *\r\n" -" * Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca)\r\n" -" */\r\n" -"\r\n" -"#ifndef WMOPS_H\r\n" -"#define WMOPS_H\r\n" -"\r\n" -"#ifndef EXIT_FAILURE\r\n" -"#include /* stdlib is needed for exit() */\r\n" -"#endif\r\n" -"\r\n" -"#ifndef EOF\r\n" -"#include /* stdio is needed for fprintf() */\r\n" -"#endif\r\n" -"\r\n" -"\r\n" -"/* To Prevent \"warning: '$' in identifier or number\" message under GCC */\r\n" -"#ifdef __GNUC__\r\n" -"#pragma GCC system_header\r\n" -"#endif\r\n" -"\r\n" -"#ifndef INT_MAX\r\n" -"#define INT_MAX 32767\r\n" -"#endif\r\n" -"\r\n" -"#define FRAMES_PER_SECOND 50.0 \r\n" -"#define PROM_INST_SIZE 32 /* number of bits of each program instruction when stored in the PROM memory (applied only when the user selects reporting in bytes) */\r\n" -"\r\n" -"#ifdef WMOPS\r\n" -"enum instructions\r\n" -"{\r\n" -" _ADD,\r\n" -" _ABS,\r\n" -" _MULT,\r\n" -" _MAC,\r\n" -" _MOVE,\r\n" -" _STORE,\r\n" -" _LOGIC,\r\n" -" _SHIFT,\r\n" -" _BRANCH,\r\n" -" _DIV,\r\n" -" _SQRT,\r\n" -" _TRANS,\r\n" -" _FUNC,\r\n" -" _LOOP,\r\n" -" _INDIRECT,\r\n" -" _PTR_INIT,\r\n" -" _TEST,\r\n" -" _POWER,\r\n" -" _LOG,\r\n" -" _MISC,\r\n" -" NUM_INST\r\n" -"};\r\n" -"\r\n" -"#define _ADD_C 1\r\n" -"#define _ABS_C 1\r\n" -"#define _MULT_C 1\r\n" -"#define _MAC_C 1\r\n" -"#define _MOVE_C 1\r\n" -"#define _STORE_C 1\r\n" -"#define _LOGIC_C 1\r\n" -"#define _SHIFT_C 1\r\n" -"#define _BRANCH_C 4\r\n" -"#define _DIV_C 18\r\n" -"#define _SQRT_C 10\r\n" -"#define _TRANS_C 25\r\n" -"#define _FUNC_C 2 /* need to add number of arguments */\r\n" -"#define _LOOP_C 3\r\n" -"#define _INDIRECT_C 2\r\n" -"#define _PTR_INIT_C 1\r\n" -"#define _TEST_C 2\r\n" -"#define _POWER_C 25\r\n" -"#define _LOG_C 25\r\n" -"#define _MISC_C 1\r\n" -"\r\n" -"#define _ADD_P 1\r\n" -"#define _ABS_P 1\r\n" -"#define _MULT_P 1\r\n" -"#define _MAC_P 1\r\n" -"#define _MOVE_P 1\r\n" -"#define _STORE_P 0\r\n" -"#define _LOGIC_P 1\r\n" -"#define _SHIFT_P 1\r\n" -"#define _BRANCH_P 2\r\n" -"#define _DIV_P 2\r\n" -"#define _SQRT_P 2\r\n" -"#define _TRANS_P 2\r\n" -"#define _FUNC_P 2 /* need to add number of arguments */\r\n" -"#define _LOOP_P 1\r\n" -"#define _INDIRECT_P 2\r\n" -"#define _PTR_INIT_P 1\r\n" -"#define _TEST_P 1\r\n" -"#define _POWER_P 2\r\n" -"#define _LOG_P 2\r\n" -"#define _MISC_P 1\r\n" -"\r\n" -"#define ADD( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _ADD_C * ( x ) ); \\\r\n" -" inst_cnt[_ADD] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _ADD_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define ABS( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _ABS_C * ( x ) ); \\\r\n" -" inst_cnt[_ABS] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _ABS_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define MULT( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _MULT_C * ( x ) ); \\\r\n" -" inst_cnt[_MULT] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _MULT_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define MAC( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _MAC_C * ( x ) ); \\\r\n" -" inst_cnt[_MAC] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _MAC_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define MOVE( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _MOVE_C * ( x ) ); \\\r\n" -" inst_cnt[_MOVE] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _MOVE_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define STORE( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _STORE_C * ( x ) ); \\\r\n" -" inst_cnt[_STORE] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _STORE_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define LOGIC( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _LOGIC_C * ( x ) ); \\\r\n" -" inst_cnt[_LOGIC] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _LOGIC_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define SHIFT( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _SHIFT_C * ( x ) ); \\\r\n" -" inst_cnt[_SHIFT] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _SHIFT_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define BRANCH( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _BRANCH_C * ( x ) ); \\\r\n" -" inst_cnt[_BRANCH] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _BRANCH_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DIV( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _DIV_C * ( x ) ); \\\r\n" -" inst_cnt[_DIV] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _DIV_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define SQRT( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _SQRT_C * ( x ) ); \\\r\n" -" inst_cnt[_SQRT] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _SQRT_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define TRANS( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _TRANS_C * ( x ) ); \\\r\n" -" inst_cnt[_TRANS] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _TRANS_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define LOOP( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _LOOP_C * ( x ) ); \\\r\n" -" inst_cnt[_LOOP] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _LOOP_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define INDIRECT( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _INDIRECT_C * ( x ) ); \\\r\n" -" inst_cnt[_INDIRECT] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _INDIRECT_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define PTR_INIT( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _PTR_INIT_C * ( x ) ); \\\r\n" -" inst_cnt[_PTR_INIT] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _PTR_INIT_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define TEST( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _TEST_C * ( x ) ); \\\r\n" -" inst_cnt[_TEST] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _TEST_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define POWER( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _POWER_C * ( x ) ); \\\r\n" -" inst_cnt[_POWER] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _POWER_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define LOG( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _LOG_C * ( x ) ); \\\r\n" -" inst_cnt[_LOG] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _LOG_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define MISC( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _MISC_C * ( x ) ); \\\r\n" -" inst_cnt[_MISC] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _MISC_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"\r\n" -"#define FUNC( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( _FUNC_C + _MOVE_C * ( x ) ); \\\r\n" -" inst_cnt[_FUNC]++; \\\r\n" -" inst_cnt[_MOVE] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _FUNC_P + _MOVE_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"\r\n" -"#define DADD( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _ADD_C * ( x ) ); \\\r\n" -" inst_cnt[_ADD] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _ADD_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DMULT( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _MULT_C * ( x ) ); \\\r\n" -" inst_cnt[_MULT] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _MULT_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DMAC( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _MAC_C * ( x ) ); \\\r\n" -" inst_cnt[_MAC] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _MAC_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DMOVE( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _MOVE_C * ( x ) ); \\\r\n" -" inst_cnt[_MOVE] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _MOVE_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DSTORE( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _STORE_C * ( x ) ); \\\r\n" -" inst_cnt[_STORE] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _STORE_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DLOGIC( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _LOGIC_C * ( x ) ); \\\r\n" -" inst_cnt[_LOGIC] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _LOGIC_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DSHIFT( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _SHIFT_C * ( x ) ); \\\r\n" -" inst_cnt[_SHIFT] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _SHIFT_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DDIV( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _DIV_C * ( x ) ); \\\r\n" -" inst_cnt[_DIV] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _DIV_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DSQRT( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _SQRT_C * ( x ) ); \\\r\n" -" inst_cnt[_SQRT] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _SQRT_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"#define DTRANS( x ) \\\r\n" -" { \\\r\n" -" { \\\r\n" -" ops_cnt += ( 2 * _TRANS_C * ( x ) ); \\\r\n" -" inst_cnt[_TRANS] += ( x ); \\\r\n" -" { \\\r\n" -" static int pcnt; \\\r\n" -" if ( !pcnt ) \\\r\n" -" { \\\r\n" -" pcnt = 1; \\\r\n" -" prom_cnt += ( _TRANS_P * ( x ) ); \\\r\n" -" } \\\r\n" -" } \\\r\n" -" } \\\r\n" -" }\r\n" -"\r\n" -"extern double ops_cnt;\r\n" -"extern double prom_cnt;\r\n" -"extern double inst_cnt[NUM_INST];\r\n" -"\r\n" -"void reset_wmops( void );\r\n" -"void push_wmops( const char *label );\r\n" -"void pop_wmops( void );\r\n" -"void update_wmops( void );\r\n" -"void update_mem( void );\r\n" -"void print_wmops( void );\r\n" -"\r\n" -"#else /* WMOPS counting disabled */\r\n" -"\r\n" -"#define reset_wmops()\r\n" -"extern int cntr_push_pop;\r\n" -"#define push_wmops( x ) ( cntr_push_pop++ )\r\n" -"#define pop_wmops() ( cntr_push_pop-- )\r\n" -"#define update_wmops() ( assert( cntr_push_pop == 0 ) )\r\n" -"#define update_mem()\r\n" -"#define print_wmops()\r\n" -"\r\n" -"#define ADD( x )\r\n" -"#define ABS( x )\r\n" -"#define MULT( x )\r\n" -"#define MAC( x )\r\n" -"#define MOVE( x )\r\n" -"#define STORE( x )\r\n" -"#define LOGIC( x )\r\n" -"#define SHIFT( x )\r\n" -"#define BRANCH( x )\r\n" -"#define DIV( x )\r\n" -"#define SQRT( x )\r\n" -"#define TRANS( x )\r\n" -"#define FUNC( x )\r\n" -"#define LOOP( x )\r\n" -"#define INDIRECT( x )\r\n" -"#define PTR_INIT( x )\r\n" -"#define TEST( x )\r\n" -"#define POWER( x )\r\n" -"#define LOG( x )\r\n" -"#define MISC( x )\r\n" -"\r\n" -"#define DADD( x )\r\n" -"#define DMULT( x )\r\n" -"#define DMAC( x )\r\n" -"#define DMOVE( x )\r\n" -"#define DSTORE( x )\r\n" -"#define DLOGIC( x )\r\n" -"#define DSHIFT( x )\r\n" -"#define DDIV( x )\r\n" -"#define DSQRT( x )\r\n" -"#define DTRANS( x )\r\n" -"\r\n" -"#endif\r\n" -"\r\n" -"#ifndef WMOPS\r\n" -"/* DESACTIVATE the Counting Mechanism */\r\n" -"#define OP_COUNT_( op, n )\r\n" -"\r\n" -"/* DESACTIVATE Operation Counter Wrappers */\r\n" -"#define OP_COUNT_WRAPPER1_( op, val ) ( val )\r\n" -"#define OP_COUNT_WRAPPER2_( expr )\r\n" -"#define OP_COUNT_WRAPPER3_( op, expr ) expr\r\n" -"\r\n" -"/* DESACTIVATE Logical & Ternary Operators */\r\n" -"#define __\r\n" -"#define _\r\n" -"\r\n" -"#else\r\n" -"\r\n" -"/* '*ops_cnt_ptr' is Used to Avoid: \"warning: operation on 'ops_cnt' may be undefined\" with Cygwin gcc Compiler */\r\n" -"static double *ops_cnt_ptr = &ops_cnt;\r\n" -"#define OP_COUNT_( op, x ) ( *ops_cnt_ptr += ( op##_C * ( x ) ), inst_cnt[op] += ( x ) )\r\n" -"\r\n" -"/******************************************************************/\r\n" -"/* NOTES: */\r\n" -"/* The 'wmc_flag_' flag is global to avoid declaration in every */\r\n" -"/* function and 'static' to avoid clashing with other modules */\r\n" -"/* that include this header file. */\r\n" -"/* */\r\n" -"/* The declarations of 'wmc_flag_' and 'wops_' in this header */\r\n" -"/* file prevent the addition of a 'C' file to the Project. */\r\n" -"/******************************************************************/\r\n" -"\r\n" -"/* General Purpose Global Flag */\r\n" -"static int wmc_flag_ = 0;\r\n" -"\r\n" -"/* Operation Counter Wrappers */\r\n" -"#define OP_COUNT_WRAPPER1_( op, val ) ( op, val )\r\n" -"#define OP_COUNT_WRAPPER2_( expr ) \\\r\n" -" if ( expr, 0 ) \\\r\n" -" ; \\\r\n" -" else\r\n" -"#define OP_COUNT_WRAPPER3_( op, expr ) \\\r\n" -" if ( op, 0 ) \\\r\n" -" ; \\\r\n" -" else \\\r\n" -" expr\r\n" -"\r\n" -"#endif\r\n" -"\r\n" -"/* Define all Macros without '{' & '}' (None of these should be called externally!) */\r\n" -"#define ABS_( x ) OP_COUNT_( _ABS, ( x ) )\r\n" -"#define ADD_( x ) OP_COUNT_( _ADD, ( x ) )\r\n" -"#define MULT_( x ) OP_COUNT_( _MULT, ( x ) )\r\n" -"#define MAC_( x ) OP_COUNT_( _MAC, ( x ) )\r\n" -"#define MOVE_( x ) OP_COUNT_( _MOVE, ( x ) )\r\n" -"#define STORE_( x ) OP_COUNT_( _STORE, ( x ) )\r\n" -"#define LOGIC_( x ) OP_COUNT_( _LOGIC, ( x ) )\r\n" -"#define SHIFT_( x ) OP_COUNT_( _SHIFT, ( x ) )\r\n" -"#define BRANCH_( x ) OP_COUNT_( _BRANCH, ( x ) )\r\n" -"#define DIV_( x ) OP_COUNT_( _DIV, ( x ) )\r\n" -"#define SQRT_( x ) OP_COUNT_( _SQRT, ( x ) )\r\n" -"#define TRANS_( x ) OP_COUNT_( _TRANS, ( x ) )\r\n" -"#define POWER_( x ) TRANS_( x )\r\n" -"#define LOG_( x ) TRANS_( x )\r\n" -"#define LOOP_( x ) OP_COUNT_( _LOOP, ( x ) )\r\n" -"#define INDIRECT_( x ) OP_COUNT_( _INDIRECT, ( x ) )\r\n" -"#define PTR_INIT_( x ) OP_COUNT_( _PTR_INIT, ( x ) )\r\n" -"#define FUNC_( x ) ( OP_COUNT_( _MOVE, ( x ) ), OP_COUNT_( _FUNC, 1 ) )\r\n" -"#define MISC_( x ) ABS_( x )\r\n" -"\r\n" -"/* Math Operations */\r\n" -"#define abs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), abs )\r\n" -"#define fabs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabs )\r\n" -"#define fabsf_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabsf )\r\n" -"#define labs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), labs )\r\n" -"#define floor_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floor )\r\n" -"#define floorf_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floorf )\r\n" -"#define sqrt_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrt )\r\n" -"#define sqrtf_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrtf )\r\n" -"#define pow_ OP_COUNT_WRAPPER1_( POWER_( 1 ), pow )\r\n" -"#define powf_ OP_COUNT_WRAPPER1_( POWER_( 1 ), powf )\r\n" -"#define exp_ OP_COUNT_WRAPPER1_( POWER_( 1 ), exp )\r\n" -"#define expf_ OP_COUNT_WRAPPER1_( POWER_( 1 ), expf )\r\n" -"#define log_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log )\r\n" -"#define logf_ OP_COUNT_WRAPPER1_( LOG_( 1 ), logf )\r\n" -"#define log10_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10 )\r\n" -"#define log10f_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10f )\r\n" -"#define cos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cos )\r\n" -"#define cosf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosf )\r\n" -"#define sin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sin )\r\n" -"#define sinf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinf )\r\n" -"#define tan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tan )\r\n" -"#define tanf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanf )\r\n" -"#define acos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acos )\r\n" -"#define acosf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acosf )\r\n" -"#define asin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asin )\r\n" -"#define asinf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asinf )\r\n" -"#define atan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan )\r\n" -"#define atanf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atanf )\r\n" -"#define atan2_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2 )\r\n" -"#define atan2f_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2f )\r\n" -"#define cosh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosh )\r\n" -"#define coshf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), coshf )\r\n" -"#define sinh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinh )\r\n" -"#define sinhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinhf )\r\n" -"#define tanh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanh )\r\n" -"#define tanhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanhf )\r\n" -"#define fmod_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmod )\r\n" -"#define fmodf_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmodf )\r\n" -"#define frexp_ OP_COUNT_WRAPPER1_( MISC_( 2 ), frexp )\r\n" -"#define frexpf_ OP_COUNT_WRAPPER1_( MISC_( 2 ), frexpf )\r\n" -"\r\n" -"/* the macros below are instrumented versions of user-defined macros that might be used in the source code \r\n" -"/* representing some well-known and recognized mathematical operations (that are not defined in math.h) */\r\n" -"/* Note: the 'wmc_flag_=wmc_flag_' is used to avoid warning: left-hand operand of comma expression has no effect with gcc */\r\n" -"\r\n" -"#define min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), min( ( a ), ( b ) ) )\r\n" -"#define max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), max( ( a ), ( b ) ) )\r\n" -"#define MIN_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MIN( ( a ), ( b ) ) )\r\n" -"#define MAX_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MAX( ( a ), ( b ) ) )\r\n" -"#define Min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Min( ( a ), ( b ) ) )\r\n" -"#define Max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Max( ( a ), ( b ) ) )\r\n" -"#define sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), sqr( ( x ) ) )\r\n" -"#define Sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Sqr( ( x ) ) )\r\n" -"#define SQR_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQR( ( x ) ) )\r\n" -"#define square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), square( ( x ) ) )\r\n" -"#define Square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Square( ( x ) ) )\r\n" -"#define SQUARE_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQUARE( ( x ) ) )\r\n" -"#define sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), sign( ( x ) ) )\r\n" -"#define Sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), Sign( ( x ) ) )\r\n" -"#define SIGN_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), SIGN( ( x ) ) )\r\n" -"#define inv_sqrt_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) )\r\n" -"#define inv_sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrtf( ( x ) ) )\r\n" -"#define log_base_2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log_base_2( ( x ) ) )\r\n" -"#define log2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2( ( x ) ) )\r\n" -"#define log2f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2f( ( x ) ) )\r\n" -"#define log2_f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2_f( ( x ) ) )\r\n" -"#define _round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _round( ( x ) ) )\r\n" -"#define round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round( ( x ) ) )\r\n" -"#define round_f_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round_f( ( x ) ) )\r\n" -"#define roundf_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, roundf( ( x ) ) )\r\n" -"#define set_min_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_min( ( a ), ( b ) ) )\r\n" -"#define set_max_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_max( ( a ), ( b ) ) )\r\n" -"\r\n" -"/* Functions */\r\n" -"#define func_( name, x ) OP_COUNT_WRAPPER1_( FUNC_( x ), name )\r\n" -"\r\n" -"/* Logical Operators */\r\n" -"#ifndef __\r\n" -"#define __ ( BRANCH_( 1 ), 1 ) &&\r\n" -"#endif\r\n" -"\r\n" -"/* Ternary Operators (? and :) */\r\n" -"#ifndef _\r\n" -"#define _ ( BRANCH_( 1 ), 0 ) ? 0:\r\n" -"#endif\r\n" -"\r\n" -"/* Flow Control keywords */\r\n" -"#define if_ \\\r\n" -" OP_COUNT_WRAPPER2_( BRANCH_( 1 ) ) \\\r\n" -" if\r\n" -"#define for_ OP_COUNT_WRAPPER2_( LOOP_(1)) for\r\n" -"#define while_( c ) \\\r\n" -" while \\\r\n" -" OP_COUNT_WRAPPER1_( BRANCH_( 1 ), ( c ) ) /* needs extra \"()\" if ',' encountered */\r\n" -"#define do_ \\\r\n" -" do \\\r\n" -" {\r\n" -"#define _while \\\r\n" -" BRANCH_( 1 ); \\\r\n" -" } \\\r\n" -" while\r\n" -"\r\n" -"#define goto_ \\\r\n" -" OP_COUNT_WRAPPER2_( BRANCH_( 1 ) ) \\\r\n" -" goto\r\n" -"#define break_ \\\r\n" -" OP_COUNT_WRAPPER2_( BRANCH_( 1 ) ) \\\r\n" -" break\r\n" -"#define continue_ \\\r\n" -" OP_COUNT_WRAPPER2_( BRANCH_( 1 ) ) \\\r\n" -" continue\r\n" -"#define return_ \\\r\n" -" OP_COUNT_WRAPPER2_( ( wmc_flag_ = stack_tree_level_, STACK_DEPTH_FCT_RETURN ) ) \\\r\n" -" return\r\n" -"\r\n" -"#define switch_ \\\r\n" -" OP_COUNT_WRAPPER2_( ( BRANCH_( 1 ), wmc_flag_ = 1 ) ) \\\r\n" -" switch\r\n" -"#define cost_( n ) OP_COUNT_WRAPPER2_( wmc_flag_ ? ( ADD_( n ), BRANCH_( n ), wmc_flag_ = 0 ) : 0 );\r\n" -"\r\n" -"#ifdef WMOPS\r\n" -"\r\n" -"#define ACC 2\r\n" -"#define MUL 1\r\n" -"\r\n" -"/* Counting Function (should not be called externally!) */\r\n" -"static void wops_( const char *ops )\r\n" -"{\r\n" -" char lm = 0; /* lm: Last Operation is Math */\r\n" -" static char lo = 0; /* Last Operation */\r\n" -"\r\n" -" void ( *fct )( const char *ops ) = wops_;\r\n" -"\r\n" -"st:\r\n" -" while ( *ops != '\\0' )\r\n" -" {\r\n" -" switch ( *ops++ )\r\n" -" {\r\n" -" int cnt;\r\n" -" case '-':\r\n" -" for ( cnt = 0; ops[cnt] == '>'; cnt++ )\r\n" -" ;\r\n" -" if ( cnt & 1 )\r\n" -" goto ind;\r\n" -" case '+':\r\n" -" lm = 2;\r\n" -" if ( lo & MUL )\r\n" -" {\r\n" -" MULT_( -1 );\r\n" -" MAC_( 1 );\r\n" -" break;\r\n" -" }\r\n" -" lo = ACC << 2;\r\n" -" case 'U':\r\n" -" case 'D':\r\n" -" ADD_( 1 );\r\n" -" break;\r\n" -" case '*':\r\n" -" lm = 2;\r\n" -" if ( lo & ACC )\r\n" -" {\r\n" -" ADD_( -1 );\r\n" -" MAC_( 1 );\r\n" -" break;\r\n" -" }\r\n" -" lo = MUL << 2;\r\n" -" MULT_( 1 );\r\n" -" break;\r\n" -" case '/':\r\n" -" case '%':\r\n" -" lm = 2;\r\n" -" DIV_( 1 );\r\n" -" break;\r\n" -" case '&':\r\n" -" case '|':\r\n" -" case '^':\r\n" -" lm = 2;\r\n" -" case '~':\r\n" -" LOGIC_( 1 );\r\n" -" break;\r\n" -" case '<':\r\n" -" case '>':\r\n" -" if ( *ops != ops[-1] )\r\n" -" goto error;\r\n" -" ops++;\r\n" -" case -85:\r\n" -" case -69:\r\n" -" lm = 2;\r\n" -" SHIFT_( 1 );\r\n" -" break;\r\n" -" case 'L':\r\n" -" case 'G':\r\n" -" if ( *ops == 't' )\r\n" -" goto comp;\r\n" -" case 'E':\r\n" -" case 'N':\r\n" -" if ( *ops != 'e' )\r\n" -" goto error;\r\n" -" comp:\r\n" -" ops++;\r\n" -" ADD_( 1 );\r\n" -" break;\r\n" -" case '!':\r\n" -" MISC_( 2 );\r\n" -" break;\r\n" -" case 'M':\r\n" -" MOVE_( 1 );\r\n" -" break;\r\n" -" case 'S':\r\n" -" STORE_( 1 );\r\n" -" break;\r\n" -" case 'P':\r\n" -" PTR_INIT_( 1 );\r\n" -" break;\r\n" -" case '[':\r\n" -" case ']':\r\n" -" goto st;\r\n" -" ind:\r\n" -" ops++;\r\n" -" case 'I':\r\n" -" case '.':\r\n" -" INDIRECT_( 1 );\r\n" -" break;\r\n" -" case '=':\r\n" -" if ( lm )\r\n" -" goto st;\r\n" -" case '\\0':\r\n" -" /* This Shouldn't Happen */\r\n" -" /* These are Used to Avoid: \"warning: 'name' defined but not used\" with Cygwin gcc Compiler */\r\n" -" wmc_flag_ = wmc_flag_;\r\n" -" ops_cnt_ptr = ops_cnt_ptr;\r\n" -" fct( \"\" );\r\n" -" error:\r\n" -" default:\r\n" -" fprintf( stderr, \"\\r wops: Invalid Counting Operation '%s'\\n\", ops - 1 );\r\n" -" exit( -1 );\r\n" -" }\r\n" -" lm >>= 1;\r\n" -" lo >>= 2;\r\n" -" }\r\n" -"\r\n" -" return;\r\n" -"}\r\n" -"\r\n" -"#endif\r\n" -"\r\n" -"/* All Other Operations */\r\n" -"#define $( str ) OP_COUNT_WRAPPER2_( wops_( str ) )\r\n" -"\r\n" -"\r\n" -"/*-------------------------------------------------------------------*\r\n" -" * Memory counting tool\r\n" -" *-------------------------------------------------------------------*/\r\n" -"\r\n" -"/* Enhanced Const Data Size Counting (Rounding Up to the Nearest 'Integer' Size) */\r\n" -"#define rsize( item ) ( ( sizeof( item ) + sizeof( int ) - 1 ) / sizeof( int ) * sizeof( int ) )\r\n" -"\r\n" -"#ifdef _MSC_VER\r\n" -"/* Disable \"warning C4210: nonstandard extension used : function given file scope\" with Visual Studio Compiler */\r\n" -"#pragma warning( disable : 4210 )\r\n" -"#endif\r\n" -"\r\n" -"/* Const Data Size and PROM Size Wrapper Functions */\r\n" -"#define Const_Data_Size_Func( file ) Const_Data_Size_##file( void )\r\n" -"#define Get_Const_Data_Size( file, val_ptr ) \\\r\n" -" { \\\r\n" -" extern int Const_Data_Size_##file( void ); \\\r\n" -" *( val_ptr ) = Const_Data_Size_##file(); \\\r\n" -" }\r\n" -"#define PROM_Size_Func( file ) PROM_Size_##file( void )\r\n" -"#define Get_PROM_Size( file, val_ptr ) \\\r\n" -" { \\\r\n" -" int PROM_Size_##file( void ); \\\r\n" -" *( val_ptr ) = PROM_Size_##file(); \\\r\n" -" }\r\n" -"\r\n" -"/* ROM Size Lookup Table - contains information about PROM size and Const Data Size in all source files */\r\n" -"/* The print_mem() function looks for this table to print the results of Const Data usage and PROM usage */\r\n" -"typedef struct ROM_Size_Lookup_Table\r\n" -"{\r\n" -" const char file_spec[255];\r\n" -" int PROM_size;\r\n" -" int ( *Get_Const_Data_Size_Func )( void );\r\n" -"} ROM_Size_Lookup_Table;\r\n" -"\r\n" -"/* The WMC tool inserts the following declaration during the innstrumentation process in the .c file where the function print_mem() is located */\r\n" -"/* and modifies it to print_mem(Const_Data_PROM_Table) */\r\n" -"\r\n" -"/* #ifdef WMOPS\r\n" -" * ROM_Size_Lookup_Table Const_Data_PROM_Table[] =\r\n" -" * {\r\n" -" * {\"../lib_enc/rom_enc.c\", 0, NULL},\r\n" -" * {\"../lib_com/*.c\", 0, NULL},\r\n" -" * {\"\", -1, NULL}\r\n" -" * };\r\n" -" * #endif\r\n" -" */\r\n" -"\r\n" -"/*#define MEM_ALIGN_64BITS */ /* Define this when using 64 Bits values in the code (ex: double), otherwise it will align on 32 Bits */\r\n" -"/*#define MEM_COUNT_DETAILS*/\r\n" -"\r\n" -"typedef enum\r\n" -"{\r\n" -" USE_BYTES = 0,\r\n" -" USE_16BITS = 1,\r\n" -" USE_32BITS = 2,\r\n" -" USE_64BITS = 3\r\n" -"} Counting_Size;\r\n" -"\r\n" -"#if ( defined( _WIN32 ) && ( _MSC_VER <= 1800 ) && ( _MSC_VER >= 1300 ) )\r\n" -"#define __func__ __FUNCTION__\r\n" -"#elif defined( __STDC_VERSION__ ) && __STDC_VERSION__ < 199901L\r\n" -"#if ( __GNUC__ >= 2 )\r\n" -"#define __func__ __FUNCTION__\r\n" -"#else\r\n" -"#define __func__ \"\"\r\n" -"#endif\r\n" -"#elif defined( __GNUC__ )\r\n" -"#define __func__ __extension__ __FUNCTION__\r\n" -"#endif\r\n" -"\r\n" -"\r\n" -"#ifdef WMOPS\r\n" -"\r\n" -"void *mem_alloc( const char *func_name, int func_lineno, size_t size, char *alloc_str );\r\n" -"void mem_free( const char *func_name, int func_lineno, void *ptr );\r\n" -"\r\n" -"#define malloc_( size ) mem_alloc( __func__, __LINE__, size, \"m:\" #size )\r\n" -"#define calloc_( n, size ) mem_alloc( __func__, __LINE__, ( n ) * ( size ), \"c:\" #n \", \" #size )\r\n" -"#define free_( ptr ) mem_free( __func__, __LINE__, ptr )\r\n" -"\r\n" -"void reset_mem( Counting_Size cnt_size );\r\n" -"void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] );\r\n" -"\r\n" -"int push_stack( const char *filename, const char *fctname );\r\n" -"int pop_stack( const char *filename, const char *fctname );\r\n" -"\r\n" -"#ifdef WMOPS_DETAIL\r\n" -"#define STACK_DEPTH_FCT_CALL ( push_wmops( __FUNCTION__ \" [WMC_AUTO]\" ), push_stack( __FILE__, __FUNCTION__ ) ) /* add push_wmops() in all function calls */\r\n" -"#define STACK_DEPTH_FCT_RETURN ( pop_wmops(), pop_stack( __FILE__, __FUNCTION__ ) ) /* add pop_wmops() in all function returns */\r\n" -"#else\r\n" -"#define STACK_DEPTH_FCT_CALL push_stack( __FILE__, __FUNCTION__ )\r\n" -"#define STACK_DEPTH_FCT_RETURN pop_stack( __FILE__, __FUNCTION__ )\r\n" -"#endif\r\n" -"\r\n" -"void reset_stack( void );\r\n" -"#define func_start_ int stack_tree_level_ = STACK_DEPTH_FCT_CALL;\r\n" -"\r\n" -"#else\r\n" -"#define malloc_( n1 ) malloc( n1 )\r\n" -"#define calloc_( n1, n2 ) calloc( n1, n2 )\r\n" -"#define free_( ptr ) free( ptr )\r\n" -"#define reset_mem( cnt_size )\r\n" -"#define print_mem( Const_Data_PROM_Table )\r\n" -"\r\n" -"#define push_stack( file, fct )\r\n" -"#define pop_stack( file, fct )\r\n" -"#define reset_stack()\r\n" -"#define func_start_\r\n" -"\r\n" -"#endif\r\n" -"\r\n" -"\r\n" -"/* Global counter variable for calculation of complexity weight */\r\n" -"typedef struct\r\n" -"{\r\n" -" unsigned int add; /* Complexity Weight of 1 */\r\n" -" unsigned int sub; /* Complexity Weight of 1 */\r\n" -" unsigned int abs_s; /* Complexity Weight of 1 */\r\n" -" unsigned int shl; /* Complexity Weight of 1 */\r\n" -" unsigned int shr; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int extract_h; /* Complexity Weight of 1 */\r\n" -" unsigned int extract_l; /* Complexity Weight of 1 */\r\n" -" unsigned int mult; /* Complexity Weight of 1 */\r\n" -" unsigned int L_mult; /* Complexity Weight of 1 */\r\n" -" unsigned int negate; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int round; /* Complexity Weight of 1 */\r\n" -" unsigned int L_mac; /* Complexity Weight of 1 */\r\n" -" unsigned int L_msu; /* Complexity Weight of 1 */\r\n" -" unsigned int L_macNs; /* Complexity Weight of 1 */\r\n" -" unsigned int L_msuNs; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L_add; /* Complexity Weight of 1 */\r\n" -" unsigned int L_sub; /* Complexity Weight of 1 */\r\n" -" unsigned int L_add_c; /* Complexity Weight of 2 */\r\n" -" unsigned int L_sub_c; /* Complexity Weight of 2 */\r\n" -" unsigned int L_negate; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L_shl; /* Complexity Weight of 1 */\r\n" -" unsigned int L_shr; /* Complexity Weight of 1 */\r\n" -" unsigned int mult_r; /* Complexity Weight of 1 */\r\n" -" unsigned int shr_r; /* Complexity Weight of 3 */\r\n" -" unsigned int mac_r; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int msu_r; /* Complexity Weight of 1 */\r\n" -" unsigned int L_deposit_h; /* Complexity Weight of 1 */\r\n" -" unsigned int L_deposit_l; /* Complexity Weight of 1 */\r\n" -" unsigned int L_shr_r; /* Complexity Weight of 3 */\r\n" -" unsigned int L_abs; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L_sat; /* Complexity Weight of 4 */\r\n" -" unsigned int norm_s; /* Complexity Weight of 1 */\r\n" -" unsigned int div_s; /* Complexity Weight of 18 */\r\n" -" unsigned int norm_l; /* Complexity Weight of 1 */\r\n" -" unsigned int move16; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int move32; /* Complexity Weight of 2 */\r\n" -" unsigned int Logic16; /* Complexity Weight of 1 */\r\n" -" unsigned int Logic32; /* Complexity Weight of 2 */\r\n" -" unsigned int Test; /* Complexity Weight of 2 */\r\n" -" unsigned int s_max; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int s_min; /* Complexity Weight of 1 */\r\n" -" unsigned int L_max; /* Complexity Weight of 1 */\r\n" -" unsigned int L_min; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_max; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_min; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int shl_r; /* Complexity Weight of 3 */\r\n" -" unsigned int L_shl_r; /* Complexity Weight of 3 */\r\n" -" unsigned int L40_shr_r; /* Complexity Weight of 3 */\r\n" -" unsigned int L40_shl_r; /* Complexity Weight of 3 */\r\n" -" unsigned int norm_L40; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L40_shl; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_shr; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_negate; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_add; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_sub; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L40_abs; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_mult; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_mac; /* Complexity Weight of 1 */\r\n" -" unsigned int mac_r40; /* Complexity Weight of 2 */\r\n" -"\r\n" -" unsigned int L40_msu; /* Complexity Weight of 1 */\r\n" -" unsigned int msu_r40; /* Complexity Weight of 2 */\r\n" -" unsigned int Mpy_32_16_ss; /* Complexity Weight of 2 */\r\n" -" unsigned int Mpy_32_32_ss; /* Complexity Weight of 4 */\r\n" -" unsigned int L_mult0; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L_mac0; /* Complexity Weight of 1 */\r\n" -" unsigned int L_msu0; /* Complexity Weight of 1 */\r\n" -" unsigned int lshl; /* Complexity Weight of 1 */\r\n" -" unsigned int lshr; /* Complexity Weight of 1 */\r\n" -" unsigned int L_lshl; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L_lshr; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_lshl; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_lshr; /* Complexity Weight of 1 */\r\n" -" unsigned int s_and; /* Complexity Weight of 1 */\r\n" -" unsigned int s_or; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int s_xor; /* Complexity Weight of 1 */\r\n" -" unsigned int L_and; /* Complexity Weight of 1 */\r\n" -" unsigned int L_or; /* Complexity Weight of 1 */\r\n" -" unsigned int L_xor; /* Complexity Weight of 1 */\r\n" -" unsigned int rotl; /* Complexity Weight of 3 */\r\n" -"\r\n" -" unsigned int rotr; /* Complexity Weight of 3 */\r\n" -" unsigned int L_rotl; /* Complexity Weight of 3 */\r\n" -" unsigned int L_rotr; /* Complexity Weight of 3 */\r\n" -" unsigned int L40_set; /* Complexity Weight of 3 */\r\n" -" unsigned int L40_deposit_h; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L40_deposit_l; /* Complexity Weight of 1 */\r\n" -" unsigned int L40_deposit32; /* Complexity Weight of 1 */\r\n" -" unsigned int Extract40_H; /* Complexity Weight of 1 */\r\n" -" unsigned int Extract40_L; /* Complexity Weight of 1 */\r\n" -" unsigned int L_Extract40; /* Complexity Weight of 1 */\r\n" -"\r\n" -" unsigned int L40_round; /* Complexity Weight of 1 */\r\n" -" unsigned int L_saturate40; /* Complexity Weight of 1 */\r\n" -" unsigned int round40; /* Complexity Weight of 1 */\r\n" -" unsigned int If; /* Complexity Weight of 4 */\r\n" -" unsigned int Goto; /* Complexity Weight of 4 */\r\n" -"\r\n" -" unsigned int Break; /* Complexity Weight of 4 */\r\n" -" unsigned int Switch; /* Complexity Weight of 8 */\r\n" -" unsigned int For; /* Complexity Weight of 3 */\r\n" -" unsigned int While; /* Complexity Weight of 4 */\r\n" -" unsigned int Continue; /* Complexity Weight of 4 */\r\n" -"\r\n" -" unsigned int L_mls; /* Complexity Weight of 6 */\r\n" -" unsigned int div_l; /* Complexity Weight of 32 */\r\n" -" unsigned int i_mult; /* Complexity Weight of 3 */\r\n" -"} BASIC_OP;\r\n" -"\r\n" -"#ifdef WMOPS\r\n" -"extern BASIC_OP *multiCounter;\r\n" -"extern int currCounter;\r\n" -"\r\n" -"/* Technical note :\r\n" -" * The following 3 variables are only used for correct complexity\r\n" -" * evaluation of the following structure :\r\n" -" * IF{\r\n" -" * ...\r\n" -" * } ELSE IF {\r\n" -" * ...\r\n" -" * } ELSE IF {\r\n" -" * ...\r\n" -" * }\r\n" -" * ...\r\n" -" * } ELSE {\r\n" -" * ...\r\n" -" * }\r\n" -" */\r\n" -"extern int funcId_where_last_call_to_else_occurred;\r\n" -"extern long funcid_total_wmops_at_last_call_to_else;\r\n" -"extern int call_occurred;\r\n" -"\r\n" -"extern long TotalWeightedOperation( void );\r\n" -"long DeltaWeightedOperation( void );\r\n" -"\r\n" -"void Set_BASOP_WMOPS_counter( int counterId );\r\n" -"void Reset_BASOP_WMOPS_counter( void );\r\n" -"\r\n" -"#endif\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : FOR\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro FOR should be used instead of the 'for' C statement.\r\n" -" * The complexity is independent of the number of loop iterations that are\r\n" -" * performed.\r\n" -" *\r\n" -" * Complexity weight : 3 (regardless of number of iterations).\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define FOR( a) for( a)\r\n" -"\r\n" -"#else \r\n" -"#define FOR( a) if( incrFor(), 0); else for( a)\r\n" -"\r\n" -"static __inline void incrFor( void) {\r\n" -" multiCounter[currCounter].For++;\r\n" -"}\r\n" -"#endif \r\n" -"\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : WHILE\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro WHILE should be used instead of the 'while' C statement.\r\n" -" * The complexity is proportional to the number of loop iterations that\r\n" -" * are performed.\r\n" -" *\r\n" -" * Complexity weight : 4 x 'number of loop iterations'.\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define WHILE( a) while( a)\r\n" -"\r\n" -"#else \r\n" -"#define WHILE( a) while( incrWhile(), a)\r\n" -"\r\n" -"static __inline void incrWhile( void) {\r\n" -" multiCounter[currCounter].While++;\r\n" -"}\r\n" -"#endif \r\n" -"\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : DO\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro DO should be used instead of the 'do' C statement.\r\n" -" *\r\n" -" * Complexity weight : 0 (complexity counted by WHILE macro).\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define DO do\r\n" -"\r\n" -"#else \r\n" -"#define DO do\r\n" -"\r\n" -"#endif \r\n" -"\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : IF\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro IF should :\r\n" -" *\r\n" -" * - not be used when :\r\n" -" * - the 'if' structure does not have any 'else if' nor 'else' statement\r\n" -" * - and it conditions only one DSP basic operations.\r\n" -" *\r\n" -" * - be used instead of the 'if' C statement in every other case :\r\n" -" * - when there is an 'else' or 'else if' statement,\r\n" -" * - or when the 'if' conditions several DSP basic operations,\r\n" -" * - or when the 'if' conditions a function call.\r\n" -" *\r\n" -" * Complexity weight : 4\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define IF( a) if( a)\r\n" -"\r\n" -"#else \r\n" -"#define IF( a) if( incrIf(), a)\r\n" -"\r\n" -"static __inline void incrIf( void) {\r\n" -" /* Technical note :\r\n" -" * If the \"IF\" operator comes just after an \"ELSE\", its counter\r\n" -" * must not be incremented.\r\n" -" */\r\n" -" if ( ( currCounter != funcId_where_last_call_to_else_occurred ) || ( TotalWeightedOperation() != funcid_total_wmops_at_last_call_to_else ) || ( call_occurred == 1 ) )\r\n" -" {\r\n" -" multiCounter[currCounter].If++;\r\n" -" }\r\n" -"\r\n" -" call_occurred = 0;\r\n" -" funcId_where_last_call_to_else_occurred = INT_MAX;\r\n" -"}\r\n" -"#endif \r\n" -"\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : ELSE\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro ELSE should be used instead of the 'else' C statement.\r\n" -" *\r\n" -" * Complexity weight : 4\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define ELSE else\r\n" -"\r\n" -"#else \r\n" -"#define ELSE else if( incrElse(), 0) ; else\r\n" -"\r\n" -"static __inline void incrElse( void) {\r\n" -" multiCounter[currCounter].If++;\r\n" -"\r\n" -" /* We keep track of the funcId of the last function\r\n" -" * which used ELSE {...} structure.\r\n" -" */\r\n" -" funcId_where_last_call_to_else_occurred = currCounter;\r\n" -"\r\n" -" /* We keep track of the number of WMOPS of this funcId\r\n" -" * when the ELSE macro was called.\r\n" -" */\r\n" -" funcid_total_wmops_at_last_call_to_else = TotalWeightedOperation();\r\n" -"\r\n" -" /* call_occurred is set to 0, in order to count the next IF (if necessary)\r\n" -" */\r\n" -" call_occurred = 0;\r\n" -"}\r\n" -"#endif \r\n" -"\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : SWITCH\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro SWITCH should be used instead of the 'switch' C statement.\r\n" -" *\r\n" -" * Complexity weight : 8\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define SWITCH( a) switch( a)\r\n" -"\r\n" -"#else \r\n" -"#define SWITCH( a) switch( incrSwitch(), a)\r\n" -"\r\n" -"static __inline void incrSwitch( void) {\r\n" -" multiCounter[currCounter].Switch++;\r\n" -"}\r\n" -"#endif \r\n" -"\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : CONTINUE\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro CONTINUE should be used instead of the 'continue' C statement.\r\n" -" *\r\n" -" * Complexity weight : 4\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define CONTINUE continue\r\n" -"\r\n" -"#else \r\n" -"#define CONTINUE if( incrContinue(), 0); else continue\r\n" -"\r\n" -"static __inline void incrContinue( void) {\r\n" -" multiCounter[currCounter].Continue++;\r\n" -"}\r\n" -"#endif \r\n" -"\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : BREAK\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro BREAK should be used instead of the 'break' C statement.\r\n" -" *\r\n" -" * Complexity weight : 4\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define BREAK break\r\n" -"\r\n" -"#else \r\n" -"#define BREAK if( incrBreak(), 0) break; else break\r\n" -"\r\n" -"static __inline void incrBreak( void) {\r\n" -" multiCounter[currCounter].Break++;\r\n" -"}\r\n" -"#endif \r\n" -"\r\n" -"\r\n" -"/*****************************************************************************\r\n" -" *\r\n" -" * Function Name : GOTO\r\n" -" *\r\n" -" * Purpose :\r\n" -" *\r\n" -" * The macro GOTO should be used instead of the 'goto' C statement.\r\n" -" *\r\n" -" * Complexity weight : 4\r\n" -" *\r\n" -" *****************************************************************************/\r\n" -"#ifndef WMOPS\r\n" -"#define GOTO goto\r\n" -"\r\n" -"#else \r\n" -"#define GOTO if( incrGoto(), 0); else goto\r\n" -"\r\n" -"static __inline void incrGoto( void) {\r\n" -" multiCounter[currCounter].Goto++;\r\n" -"}\r\n" -"#endif \r\n" -"\r\n" -"#endif /* WMOPS_H */\r\n" -"\r\n" -"\r\n" -"\r\n" \ No newline at end of file +"/*\r\n", +" * (C) 2023 copyright VoiceAge Corporation. All Rights Reserved.\r\n", +" *\r\n", +" * This software is protected by copyright law and by international treaties. The source code, and all of its derivations,\r\n", +" * is provided by VoiceAge Corporation under the \"ITU-T Software Tools' General Public License\". Please, read the license file\r\n", +" * or refer to ITU-T Recommendation G.191 on \"SOFTWARE TOOLS FOR SPEECH AND AUDIO CODING STANDARDS\".\r\n", +" *\r\n", +" * Any use of this software is permitted provided that this notice is not removed and that neither the authors nor\r\n", +" * VoiceAge Corporation are deemed to have made any representations as to the suitability of this software\r\n", +" * for any purpose nor are held responsible for any defects of this software. THERE IS NO WARRANTY FOR THIS SOFTWARE.\r\n", +" *\r\n", +" * Authors: Guy Richard, Vladimir Malenovsky (Vladimir.Malenovsky@USherbrooke.ca)\r\n", +" */\r\n", +"\r\n", +"#ifndef WMOPS_H\r\n", +"#define WMOPS_H\r\n", +"\r\n", +"#ifndef EXIT_FAILURE\r\n", +"#include /* stdlib is needed for exit() */\r\n", +"#endif\r\n", +"\r\n", +"#ifndef EOF\r\n", +"#include /* stdio is needed for fprintf() */\r\n", +"#endif\r\n", +"\r\n", +"\r\n", +"/* To Prevent \"warning: '$' in identifier or number\" message under GCC */\r\n", +"#ifdef __GNUC__\r\n", +"#pragma GCC system_header\r\n", +"#endif\r\n", +"\r\n", +"#ifndef INT_MAX\r\n", +"#define INT_MAX 32767\r\n", +"#endif\r\n", +"\r\n", +"#define FRAMES_PER_SECOND 50.0 \r\n", +"#define PROM_INST_SIZE 32 /* number of bits of each program instruction when stored in the PROM memory (applied only when the user selects reporting in bytes) */\r\n", +"\r\n", +"#ifdef WMOPS\r\n", +"enum instructions\r\n", +"{\r\n", +" _ADD,\r\n", +" _ABS,\r\n", +" _MULT,\r\n", +" _MAC,\r\n", +" _MOVE,\r\n", +" _STORE,\r\n", +" _LOGIC,\r\n", +" _SHIFT,\r\n", +" _BRANCH,\r\n", +" _DIV,\r\n", +" _SQRT,\r\n", +" _TRANS,\r\n", +" _FUNC,\r\n", +" _LOOP,\r\n", +" _INDIRECT,\r\n", +" _PTR_INIT,\r\n", +" _TEST,\r\n", +" _POWER,\r\n", +" _LOG,\r\n", +" _MISC,\r\n", +" NUM_INST\r\n", +"};\r\n", +"\r\n", +"#define _ADD_C 1\r\n", +"#define _ABS_C 1\r\n", +"#define _MULT_C 1\r\n", +"#define _MAC_C 1\r\n", +"#define _MOVE_C 1\r\n", +"#define _STORE_C 1\r\n", +"#define _LOGIC_C 1\r\n", +"#define _SHIFT_C 1\r\n", +"#define _BRANCH_C 4\r\n", +"#define _DIV_C 18\r\n", +"#define _SQRT_C 10\r\n", +"#define _TRANS_C 25\r\n", +"#define _FUNC_C 2 /* need to add number of arguments */\r\n", +"#define _LOOP_C 3\r\n", +"#define _INDIRECT_C 2\r\n", +"#define _PTR_INIT_C 1\r\n", +"#define _TEST_C 2\r\n", +"#define _POWER_C 25\r\n", +"#define _LOG_C 25\r\n", +"#define _MISC_C 1\r\n", +"\r\n", +"#define _ADD_P 1\r\n", +"#define _ABS_P 1\r\n", +"#define _MULT_P 1\r\n", +"#define _MAC_P 1\r\n", +"#define _MOVE_P 1\r\n", +"#define _STORE_P 0\r\n", +"#define _LOGIC_P 1\r\n", +"#define _SHIFT_P 1\r\n", +"#define _BRANCH_P 2\r\n", +"#define _DIV_P 2\r\n", +"#define _SQRT_P 2\r\n", +"#define _TRANS_P 2\r\n", +"#define _FUNC_P 2 /* need to add number of arguments */\r\n", +"#define _LOOP_P 1\r\n", +"#define _INDIRECT_P 2\r\n", +"#define _PTR_INIT_P 1\r\n", +"#define _TEST_P 1\r\n", +"#define _POWER_P 2\r\n", +"#define _LOG_P 2\r\n", +"#define _MISC_P 1\r\n", +"\r\n", +"#define ADD( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _ADD_C * ( x ) ); \\\r\n", +" inst_cnt[_ADD] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _ADD_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define ABS( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _ABS_C * ( x ) ); \\\r\n", +" inst_cnt[_ABS] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _ABS_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define MULT( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _MULT_C * ( x ) ); \\\r\n", +" inst_cnt[_MULT] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _MULT_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define MAC( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _MAC_C * ( x ) ); \\\r\n", +" inst_cnt[_MAC] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _MAC_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define MOVE( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _MOVE_C * ( x ) ); \\\r\n", +" inst_cnt[_MOVE] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _MOVE_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define STORE( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _STORE_C * ( x ) ); \\\r\n", +" inst_cnt[_STORE] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _STORE_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define LOGIC( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _LOGIC_C * ( x ) ); \\\r\n", +" inst_cnt[_LOGIC] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _LOGIC_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define SHIFT( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _SHIFT_C * ( x ) ); \\\r\n", +" inst_cnt[_SHIFT] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _SHIFT_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define BRANCH( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _BRANCH_C * ( x ) ); \\\r\n", +" inst_cnt[_BRANCH] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _BRANCH_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DIV( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _DIV_C * ( x ) ); \\\r\n", +" inst_cnt[_DIV] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _DIV_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define SQRT( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _SQRT_C * ( x ) ); \\\r\n", +" inst_cnt[_SQRT] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _SQRT_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define TRANS( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _TRANS_C * ( x ) ); \\\r\n", +" inst_cnt[_TRANS] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _TRANS_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define LOOP( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _LOOP_C * ( x ) ); \\\r\n", +" inst_cnt[_LOOP] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _LOOP_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define INDIRECT( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _INDIRECT_C * ( x ) ); \\\r\n", +" inst_cnt[_INDIRECT] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _INDIRECT_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define PTR_INIT( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _PTR_INIT_C * ( x ) ); \\\r\n", +" inst_cnt[_PTR_INIT] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _PTR_INIT_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define TEST( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _TEST_C * ( x ) ); \\\r\n", +" inst_cnt[_TEST] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _TEST_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define POWER( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _POWER_C * ( x ) ); \\\r\n", +" inst_cnt[_POWER] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _POWER_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define LOG( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _LOG_C * ( x ) ); \\\r\n", +" inst_cnt[_LOG] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _LOG_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define MISC( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _MISC_C * ( x ) ); \\\r\n", +" inst_cnt[_MISC] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _MISC_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"\r\n", +"#define FUNC( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( _FUNC_C + _MOVE_C * ( x ) ); \\\r\n", +" inst_cnt[_FUNC]++; \\\r\n", +" inst_cnt[_MOVE] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _FUNC_P + _MOVE_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"\r\n", +"#define DADD( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _ADD_C * ( x ) ); \\\r\n", +" inst_cnt[_ADD] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _ADD_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DMULT( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _MULT_C * ( x ) ); \\\r\n", +" inst_cnt[_MULT] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _MULT_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DMAC( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _MAC_C * ( x ) ); \\\r\n", +" inst_cnt[_MAC] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _MAC_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DMOVE( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _MOVE_C * ( x ) ); \\\r\n", +" inst_cnt[_MOVE] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _MOVE_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DSTORE( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _STORE_C * ( x ) ); \\\r\n", +" inst_cnt[_STORE] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _STORE_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DLOGIC( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _LOGIC_C * ( x ) ); \\\r\n", +" inst_cnt[_LOGIC] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _LOGIC_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DSHIFT( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _SHIFT_C * ( x ) ); \\\r\n", +" inst_cnt[_SHIFT] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _SHIFT_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DDIV( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _DIV_C * ( x ) ); \\\r\n", +" inst_cnt[_DIV] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _DIV_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DSQRT( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _SQRT_C * ( x ) ); \\\r\n", +" inst_cnt[_SQRT] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _SQRT_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"#define DTRANS( x ) \\\r\n", +" { \\\r\n", +" { \\\r\n", +" ops_cnt += ( 2 * _TRANS_C * ( x ) ); \\\r\n", +" inst_cnt[_TRANS] += ( x ); \\\r\n", +" { \\\r\n", +" static int pcnt; \\\r\n", +" if ( !pcnt ) \\\r\n", +" { \\\r\n", +" pcnt = 1; \\\r\n", +" prom_cnt += ( _TRANS_P * ( x ) ); \\\r\n", +" } \\\r\n", +" } \\\r\n", +" } \\\r\n", +" }\r\n", +"\r\n", +"extern double ops_cnt;\r\n", +"extern double prom_cnt;\r\n", +"extern double inst_cnt[NUM_INST];\r\n", +"\r\n", +"void reset_wmops( void );\r\n", +"void push_wmops( const char *label );\r\n", +"void pop_wmops( void );\r\n", +"void update_wmops( void );\r\n", +"void update_mem( void );\r\n", +"void print_wmops( void );\r\n", +"\r\n", +"#else /* WMOPS counting disabled */\r\n", +"\r\n", +"#define reset_wmops()\r\n", +"extern int cntr_push_pop;\r\n", +"#define push_wmops( x ) ( cntr_push_pop++ )\r\n", +"#define pop_wmops() ( cntr_push_pop-- )\r\n", +"#define update_wmops() ( assert( cntr_push_pop == 0 ) )\r\n", +"#define update_mem()\r\n", +"#define print_wmops()\r\n", +"\r\n", +"#define ADD( x )\r\n", +"#define ABS( x )\r\n", +"#define MULT( x )\r\n", +"#define MAC( x )\r\n", +"#define MOVE( x )\r\n", +"#define STORE( x )\r\n", +"#define LOGIC( x )\r\n", +"#define SHIFT( x )\r\n", +"#define BRANCH( x )\r\n", +"#define DIV( x )\r\n", +"#define SQRT( x )\r\n", +"#define TRANS( x )\r\n", +"#define FUNC( x )\r\n", +"#define LOOP( x )\r\n", +"#define INDIRECT( x )\r\n", +"#define PTR_INIT( x )\r\n", +"#define TEST( x )\r\n", +"#define POWER( x )\r\n", +"#define LOG( x )\r\n", +"#define MISC( x )\r\n", +"\r\n", +"#define DADD( x )\r\n", +"#define DMULT( x )\r\n", +"#define DMAC( x )\r\n", +"#define DMOVE( x )\r\n", +"#define DSTORE( x )\r\n", +"#define DLOGIC( x )\r\n", +"#define DSHIFT( x )\r\n", +"#define DDIV( x )\r\n", +"#define DSQRT( x )\r\n", +"#define DTRANS( x )\r\n", +"\r\n", +"#endif\r\n", +"\r\n", +"#ifndef WMOPS\r\n", +"/* DESACTIVATE the Counting Mechanism */\r\n", +"#define OP_COUNT_( op, n )\r\n", +"\r\n", +"/* DESACTIVATE Operation Counter Wrappers */\r\n", +"#define OP_COUNT_WRAPPER1_( op, val ) ( val )\r\n", +"#define OP_COUNT_WRAPPER2_( expr )\r\n", +"#define OP_COUNT_WRAPPER3_( op, expr ) expr\r\n", +"\r\n", +"/* DESACTIVATE Logical & Ternary Operators */\r\n", +"#define __\r\n", +"#define _\r\n", +"\r\n", +"#else\r\n", +"\r\n", +"/* '*ops_cnt_ptr' is Used to Avoid: \"warning: operation on 'ops_cnt' may be undefined\" with Cygwin gcc Compiler */\r\n", +"static double *ops_cnt_ptr = &ops_cnt;\r\n", +"#define OP_COUNT_( op, x ) ( *ops_cnt_ptr += ( op##_C * ( x ) ), inst_cnt[op] += ( x ) )\r\n", +"\r\n", +"/******************************************************************/\r\n", +"/* NOTES: */\r\n", +"/* The 'wmc_flag_' flag is global to avoid declaration in every */\r\n", +"/* function and 'static' to avoid clashing with other modules */\r\n", +"/* that include this header file. */\r\n", +"/* */\r\n", +"/* The declarations of 'wmc_flag_' and 'wops_' in this header */\r\n", +"/* file prevent the addition of a 'C' file to the Project. */\r\n", +"/******************************************************************/\r\n", +"\r\n", +"/* General Purpose Global Flag */\r\n", +"static int wmc_flag_ = 0;\r\n", +"\r\n", +"/* Operation Counter Wrappers */\r\n", +"#define OP_COUNT_WRAPPER1_( op, val ) ( op, val )\r\n", +"#define OP_COUNT_WRAPPER2_( expr ) \\\r\n", +" if ( expr, 0 ) \\\r\n", +" ; \\\r\n", +" else\r\n", +"#define OP_COUNT_WRAPPER3_( op, expr ) \\\r\n", +" if ( op, 0 ) \\\r\n", +" ; \\\r\n", +" else \\\r\n", +" expr\r\n", +"\r\n", +"#endif\r\n", +"\r\n", +"/* Define all Macros without '{' & '}' (None of these should be called externally!) */\r\n", +"#define ABS_( x ) OP_COUNT_( _ABS, ( x ) )\r\n", +"#define ADD_( x ) OP_COUNT_( _ADD, ( x ) )\r\n", +"#define MULT_( x ) OP_COUNT_( _MULT, ( x ) )\r\n", +"#define MAC_( x ) OP_COUNT_( _MAC, ( x ) )\r\n", +"#define MOVE_( x ) OP_COUNT_( _MOVE, ( x ) )\r\n", +"#define STORE_( x ) OP_COUNT_( _STORE, ( x ) )\r\n", +"#define LOGIC_( x ) OP_COUNT_( _LOGIC, ( x ) )\r\n", +"#define SHIFT_( x ) OP_COUNT_( _SHIFT, ( x ) )\r\n", +"#define BRANCH_( x ) OP_COUNT_( _BRANCH, ( x ) )\r\n", +"#define DIV_( x ) OP_COUNT_( _DIV, ( x ) )\r\n", +"#define SQRT_( x ) OP_COUNT_( _SQRT, ( x ) )\r\n", +"#define TRANS_( x ) OP_COUNT_( _TRANS, ( x ) )\r\n", +"#define POWER_( x ) TRANS_( x )\r\n", +"#define LOG_( x ) TRANS_( x )\r\n", +"#define LOOP_( x ) OP_COUNT_( _LOOP, ( x ) )\r\n", +"#define INDIRECT_( x ) OP_COUNT_( _INDIRECT, ( x ) )\r\n", +"#define PTR_INIT_( x ) OP_COUNT_( _PTR_INIT, ( x ) )\r\n", +"#define FUNC_( x ) ( OP_COUNT_( _MOVE, ( x ) ), OP_COUNT_( _FUNC, 1 ) )\r\n", +"#define MISC_( x ) ABS_( x )\r\n", +"\r\n", +"/* Math Operations */\r\n", +"#define abs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), abs )\r\n", +"#define fabs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabs )\r\n", +"#define fabsf_ OP_COUNT_WRAPPER1_( ABS_( 1 ), fabsf )\r\n", +"#define labs_ OP_COUNT_WRAPPER1_( ABS_( 1 ), labs )\r\n", +"#define floor_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floor )\r\n", +"#define floorf_ OP_COUNT_WRAPPER1_( MISC_( 1 ), floorf )\r\n", +"#define sqrt_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrt )\r\n", +"#define sqrtf_ OP_COUNT_WRAPPER1_( SQRT_( 1 ), sqrtf )\r\n", +"#define pow_ OP_COUNT_WRAPPER1_( POWER_( 1 ), pow )\r\n", +"#define powf_ OP_COUNT_WRAPPER1_( POWER_( 1 ), powf )\r\n", +"#define exp_ OP_COUNT_WRAPPER1_( POWER_( 1 ), exp )\r\n", +"#define expf_ OP_COUNT_WRAPPER1_( POWER_( 1 ), expf )\r\n", +"#define log_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log )\r\n", +"#define logf_ OP_COUNT_WRAPPER1_( LOG_( 1 ), logf )\r\n", +"#define log10_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10 )\r\n", +"#define log10f_ OP_COUNT_WRAPPER1_( LOG_( 1 ), log10f )\r\n", +"#define cos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cos )\r\n", +"#define cosf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosf )\r\n", +"#define sin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sin )\r\n", +"#define sinf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinf )\r\n", +"#define tan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tan )\r\n", +"#define tanf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanf )\r\n", +"#define acos_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acos )\r\n", +"#define acosf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), acosf )\r\n", +"#define asin_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asin )\r\n", +"#define asinf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), asinf )\r\n", +"#define atan_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan )\r\n", +"#define atanf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atanf )\r\n", +"#define atan2_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2 )\r\n", +"#define atan2f_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), atan2f )\r\n", +"#define cosh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), cosh )\r\n", +"#define coshf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), coshf )\r\n", +"#define sinh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinh )\r\n", +"#define sinhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), sinhf )\r\n", +"#define tanh_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanh )\r\n", +"#define tanhf_ OP_COUNT_WRAPPER1_( TRANS_( 1 ), tanhf )\r\n", +"#define fmod_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmod )\r\n", +"#define fmodf_ OP_COUNT_WRAPPER1_( DIV_( 1 ), fmodf )\r\n", +"#define frexp_ OP_COUNT_WRAPPER1_( MISC_( 2 ), frexp )\r\n", +"#define frexpf_ OP_COUNT_WRAPPER1_( MISC_( 2 ), frexpf )\r\n", +"\r\n", +"/* the macros below are instrumented versions of user-defined macros that might be used in the source code \r\n", +"/* representing some well-known and recognized mathematical operations (that are not defined in math.h) */\r\n", +"/* Note: the 'wmc_flag_=wmc_flag_' is used to avoid warning: left-hand operand of comma expression has no effect with gcc */\r\n", +"\r\n", +"#define min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), min( ( a ), ( b ) ) )\r\n", +"#define max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), max( ( a ), ( b ) ) )\r\n", +"#define MIN_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MIN( ( a ), ( b ) ) )\r\n", +"#define MAX_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), MAX( ( a ), ( b ) ) )\r\n", +"#define Min_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Min( ( a ), ( b ) ) )\r\n", +"#define Max_( a, b ) OP_COUNT_WRAPPER1_( MISC_( 1 ), Max( ( a ), ( b ) ) )\r\n", +"#define sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), sqr( ( x ) ) )\r\n", +"#define Sqr_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Sqr( ( x ) ) )\r\n", +"#define SQR_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQR( ( x ) ) )\r\n", +"#define square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), square( ( x ) ) )\r\n", +"#define Square_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), Square( ( x ) ) )\r\n", +"#define SQUARE_( x ) OP_COUNT_WRAPPER1_( MULT_( 1 ), SQUARE( ( x ) ) )\r\n", +"#define sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), sign( ( x ) ) )\r\n", +"#define Sign_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), Sign( ( x ) ) )\r\n", +"#define SIGN_( x ) OP_COUNT_WRAPPER1_( MOVE_( 1 ), SIGN( ( x ) ) )\r\n", +"#define inv_sqrt_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrt( ( x ) ) )\r\n", +"#define inv_sqrtf_( x ) OP_COUNT_WRAPPER1_( SQRT_( 1 ), inv_sqrtf( ( x ) ) )\r\n", +"#define log_base_2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log_base_2( ( x ) ) )\r\n", +"#define log2_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2( ( x ) ) )\r\n", +"#define log2f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2f( ( x ) ) )\r\n", +"#define log2_f_( x ) OP_COUNT_WRAPPER1_( ( LOG_( 1 ), MULT_( 1 ) ), log2_f( ( x ) ) )\r\n", +"#define _round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, _round( ( x ) ) )\r\n", +"#define round_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round( ( x ) ) )\r\n", +"#define round_f_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, round_f( ( x ) ) )\r\n", +"#define roundf_( x ) OP_COUNT_WRAPPER1_( wmc_flag_ = wmc_flag_, roundf( ( x ) ) )\r\n", +"#define set_min_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_min( ( a ), ( b ) ) )\r\n", +"#define set_max_( a, b ) OP_COUNT_WRAPPER3_( ( ADD_( 1 ), BRANCH_( 1 ), MOVE_( 1 ) ), set_max( ( a ), ( b ) ) )\r\n", +"\r\n", +"/* Functions */\r\n", +"#define func_( name, x ) OP_COUNT_WRAPPER1_( FUNC_( x ), name )\r\n", +"\r\n", +"/* Logical Operators */\r\n", +"#ifndef __\r\n", +"#define __ ( BRANCH_( 1 ), 1 ) &&\r\n", +"#endif\r\n", +"\r\n", +"/* Ternary Operators (? and :) */\r\n", +"#ifndef _\r\n", +"#define _ ( BRANCH_( 1 ), 0 ) ? 0:\r\n", +"#endif\r\n", +"\r\n", +"/* Flow Control keywords */\r\n", +"#define if_ \\\r\n", +" OP_COUNT_WRAPPER2_( BRANCH_( 1 ) ) \\\r\n", +" if\r\n", +"#define for_ OP_COUNT_WRAPPER2_( LOOP_(1)) for\r\n", +"#define while_( c ) \\\r\n", +" while \\\r\n", +" OP_COUNT_WRAPPER1_( BRANCH_( 1 ), ( c ) ) /* needs extra \"()\" if ',' encountered */\r\n", +"#define do_ \\\r\n", +" do \\\r\n", +" {\r\n", +"#define _while \\\r\n", +" BRANCH_( 1 ); \\\r\n", +" } \\\r\n", +" while\r\n", +"\r\n", +"#define goto_ \\\r\n", +" OP_COUNT_WRAPPER2_( BRANCH_( 1 ) ) \\\r\n", +" goto\r\n", +"#define break_ \\\r\n", +" OP_COUNT_WRAPPER2_( BRANCH_( 1 ) ) \\\r\n", +" break\r\n", +"#define continue_ \\\r\n", +" OP_COUNT_WRAPPER2_( BRANCH_( 1 ) ) \\\r\n", +" continue\r\n", +"#define return_ \\\r\n", +" OP_COUNT_WRAPPER2_( ( wmc_flag_ = stack_tree_level_, STACK_DEPTH_FCT_RETURN ) ) \\\r\n", +" return\r\n", +"\r\n", +"#define switch_ \\\r\n", +" OP_COUNT_WRAPPER2_( ( BRANCH_( 1 ), wmc_flag_ = 1 ) ) \\\r\n", +" switch\r\n", +"#define cost_( n ) OP_COUNT_WRAPPER2_( wmc_flag_ ? ( ADD_( n ), BRANCH_( n ), wmc_flag_ = 0 ) : 0 );\r\n", +"\r\n", +"#ifdef WMOPS\r\n", +"\r\n", +"#define ACC 2\r\n", +"#define MUL 1\r\n", +"\r\n", +"/* Counting Function (should not be called externally!) */\r\n", +"static void wops_( const char *ops )\r\n", +"{\r\n", +" char lm = 0; /* lm: Last Operation is Math */\r\n", +" static char lo = 0; /* Last Operation */\r\n", +"\r\n", +" void ( *fct )( const char *ops ) = wops_;\r\n", +"\r\n", +"st:\r\n", +" while ( *ops != '\\0' )\r\n", +" {\r\n", +" switch ( *ops++ )\r\n", +" {\r\n", +" int cnt;\r\n", +" case '-':\r\n", +" for ( cnt = 0; ops[cnt] == '>'; cnt++ )\r\n", +" ;\r\n", +" if ( cnt & 1 )\r\n", +" goto ind;\r\n", +" case '+':\r\n", +" lm = 2;\r\n", +" if ( lo & MUL )\r\n", +" {\r\n", +" MULT_( -1 );\r\n", +" MAC_( 1 );\r\n", +" break;\r\n", +" }\r\n", +" lo = ACC << 2;\r\n", +" case 'U':\r\n", +" case 'D':\r\n", +" ADD_( 1 );\r\n", +" break;\r\n", +" case '*':\r\n", +" lm = 2;\r\n", +" if ( lo & ACC )\r\n", +" {\r\n", +" ADD_( -1 );\r\n", +" MAC_( 1 );\r\n", +" break;\r\n", +" }\r\n", +" lo = MUL << 2;\r\n", +" MULT_( 1 );\r\n", +" break;\r\n", +" case '/':\r\n", +" case '%':\r\n", +" lm = 2;\r\n", +" DIV_( 1 );\r\n", +" break;\r\n", +" case '&':\r\n", +" case '|':\r\n", +" case '^':\r\n", +" lm = 2;\r\n", +" case '~':\r\n", +" LOGIC_( 1 );\r\n", +" break;\r\n", +" case '<':\r\n", +" case '>':\r\n", +" if ( *ops != ops[-1] )\r\n", +" goto error;\r\n", +" ops++;\r\n", +" case -85:\r\n", +" case -69:\r\n", +" lm = 2;\r\n", +" SHIFT_( 1 );\r\n", +" break;\r\n", +" case 'L':\r\n", +" case 'G':\r\n", +" if ( *ops == 't' )\r\n", +" goto comp;\r\n", +" case 'E':\r\n", +" case 'N':\r\n", +" if ( *ops != 'e' )\r\n", +" goto error;\r\n", +" comp:\r\n", +" ops++;\r\n", +" ADD_( 1 );\r\n", +" break;\r\n", +" case '!':\r\n", +" MISC_( 2 );\r\n", +" break;\r\n", +" case 'M':\r\n", +" MOVE_( 1 );\r\n", +" break;\r\n", +" case 'S':\r\n", +" STORE_( 1 );\r\n", +" break;\r\n", +" case 'P':\r\n", +" PTR_INIT_( 1 );\r\n", +" break;\r\n", +" case '[':\r\n", +" case ']':\r\n", +" goto st;\r\n", +" ind:\r\n", +" ops++;\r\n", +" case 'I':\r\n", +" case '.':\r\n", +" INDIRECT_( 1 );\r\n", +" break;\r\n", +" case '=':\r\n", +" if ( lm )\r\n", +" goto st;\r\n", +" case '\\0':\r\n", +" /* This Shouldn't Happen */\r\n", +" /* These are Used to Avoid: \"warning: 'name' defined but not used\" with Cygwin gcc Compiler */\r\n", +" wmc_flag_ = wmc_flag_;\r\n", +" ops_cnt_ptr = ops_cnt_ptr;\r\n", +" fct( \"\" );\r\n", +" error:\r\n", +" default:\r\n", +" fprintf( stderr, \"\\r wops: Invalid Counting Operation '%s'\\n\", ops - 1 );\r\n", +" exit( -1 );\r\n", +" }\r\n", +" lm >>= 1;\r\n", +" lo >>= 2;\r\n", +" }\r\n", +"\r\n", +" return;\r\n", +"}\r\n", +"\r\n", +"#endif\r\n", +"\r\n", +"/* All Other Operations */\r\n", +"#define $( str ) OP_COUNT_WRAPPER2_( wops_( str ) )\r\n", +"\r\n", +"\r\n", +"/*-------------------------------------------------------------------*\r\n", +" * Memory counting tool\r\n", +" *-------------------------------------------------------------------*/\r\n", +"\r\n", +"/* Enhanced Const Data Size Counting (Rounding Up to the Nearest 'Integer' Size) */\r\n", +"#define rsize( item ) ( ( sizeof( item ) + sizeof( int ) - 1 ) / sizeof( int ) * sizeof( int ) )\r\n", +"\r\n", +"#ifdef _MSC_VER\r\n", +"/* Disable \"warning C4210: nonstandard extension used : function given file scope\" with Visual Studio Compiler */\r\n", +"#pragma warning( disable : 4210 )\r\n", +"#endif\r\n", +"\r\n", +"/* Const Data Size and PROM Size Wrapper Functions */\r\n", +"#define Const_Data_Size_Func( file ) Const_Data_Size_##file( void )\r\n", +"#define Get_Const_Data_Size( file, val_ptr ) \\\r\n", +" { \\\r\n", +" extern int Const_Data_Size_##file( void ); \\\r\n", +" *( val_ptr ) = Const_Data_Size_##file(); \\\r\n", +" }\r\n", +"#define PROM_Size_Func( file ) PROM_Size_##file( void )\r\n", +"#define Get_PROM_Size( file, val_ptr ) \\\r\n", +" { \\\r\n", +" int PROM_Size_##file( void ); \\\r\n", +" *( val_ptr ) = PROM_Size_##file(); \\\r\n", +" }\r\n", +"\r\n", +"/* ROM Size Lookup Table - contains information about PROM size and Const Data Size in all source files */\r\n", +"/* The print_mem() function looks for this table to print the results of Const Data usage and PROM usage */\r\n", +"typedef struct ROM_Size_Lookup_Table\r\n", +"{\r\n", +" const char file_spec[255];\r\n", +" int PROM_size;\r\n", +" int ( *Get_Const_Data_Size_Func )( void );\r\n", +"} ROM_Size_Lookup_Table;\r\n", +"\r\n", +"/* The WMC tool inserts the following declaration during the innstrumentation process in the .c file where the function print_mem() is located */\r\n", +"/* and modifies it to print_mem(Const_Data_PROM_Table) */\r\n", +"\r\n", +"/* #ifdef WMOPS\r\n", +" * ROM_Size_Lookup_Table Const_Data_PROM_Table[] =\r\n", +" * {\r\n", +" * {\"../lib_enc/rom_enc.c\", 0, NULL},\r\n", +" * {\"../lib_com/*.c\", 0, NULL},\r\n", +" * {\"\", -1, NULL}\r\n", +" * };\r\n", +" * #endif\r\n", +" */\r\n", +"\r\n", +"/*#define MEM_ALIGN_64BITS */ /* Define this when using 64 Bits values in the code (ex: double), otherwise it will align on 32 Bits */\r\n", +"/*#define MEM_COUNT_DETAILS*/\r\n", +"\r\n", +"typedef enum\r\n", +"{\r\n", +" USE_BYTES = 0,\r\n", +" USE_16BITS = 1,\r\n", +" USE_32BITS = 2,\r\n", +" USE_64BITS = 3\r\n", +"} Counting_Size;\r\n", +"\r\n", +"#if ( defined( _WIN32 ) && ( _MSC_VER <= 1800 ) && ( _MSC_VER >= 1300 ) )\r\n", +"#define __func__ __FUNCTION__\r\n", +"#elif defined( __STDC_VERSION__ ) && __STDC_VERSION__ < 199901L\r\n", +"#if ( __GNUC__ >= 2 )\r\n", +"#define __func__ __FUNCTION__\r\n", +"#else\r\n", +"#define __func__ \"\"\r\n", +"#endif\r\n", +"#elif defined( __GNUC__ )\r\n", +"#define __func__ __extension__ __FUNCTION__\r\n", +"#endif\r\n", +"\r\n", +"\r\n", +"#ifdef WMOPS\r\n", +"\r\n", +"void *mem_alloc( const char *func_name, int func_lineno, size_t size, char *alloc_str );\r\n", +"void mem_free( const char *func_name, int func_lineno, void *ptr );\r\n", +"\r\n", +"#define malloc_( size ) mem_alloc( __func__, __LINE__, size, \"m:\" #size )\r\n", +"#define calloc_( n, size ) mem_alloc( __func__, __LINE__, ( n ) * ( size ), \"c:\" #n \", \" #size )\r\n", +"#define free_( ptr ) mem_free( __func__, __LINE__, ptr )\r\n", +"\r\n", +"void reset_mem( Counting_Size cnt_size );\r\n", +"void print_mem( ROM_Size_Lookup_Table Const_Data_PROM_Table[] );\r\n", +"\r\n", +"int push_stack( const char *filename, const char *fctname );\r\n", +"int pop_stack( const char *filename, const char *fctname );\r\n", +"\r\n", +"#ifdef WMOPS_DETAIL\r\n", +"#define STACK_DEPTH_FCT_CALL ( push_wmops( __FUNCTION__ \" [WMC_AUTO]\" ), push_stack( __FILE__, __FUNCTION__ ) ) /* add push_wmops() in all function calls */\r\n", +"#define STACK_DEPTH_FCT_RETURN ( pop_wmops(), pop_stack( __FILE__, __FUNCTION__ ) ) /* add pop_wmops() in all function returns */\r\n", +"#else\r\n", +"#define STACK_DEPTH_FCT_CALL push_stack( __FILE__, __FUNCTION__ )\r\n", +"#define STACK_DEPTH_FCT_RETURN pop_stack( __FILE__, __FUNCTION__ )\r\n", +"#endif\r\n", +"\r\n", +"void reset_stack( void );\r\n", +"#define func_start_ int stack_tree_level_ = STACK_DEPTH_FCT_CALL;\r\n", +"\r\n", +"#else\r\n", +"#define malloc_( n1 ) malloc( n1 )\r\n", +"#define calloc_( n1, n2 ) calloc( n1, n2 )\r\n", +"#define free_( ptr ) free( ptr )\r\n", +"#define reset_mem( cnt_size )\r\n", +"#define print_mem( Const_Data_PROM_Table )\r\n", +"\r\n", +"#define push_stack( file, fct )\r\n", +"#define pop_stack( file, fct )\r\n", +"#define reset_stack()\r\n", +"#define func_start_\r\n", +"\r\n", +"#endif\r\n", +"\r\n", +"\r\n", +"/* Global counter variable for calculation of complexity weight */\r\n", +"typedef struct\r\n", +"{\r\n", +" unsigned int add; /* Complexity Weight of 1 */\r\n", +" unsigned int sub; /* Complexity Weight of 1 */\r\n", +" unsigned int abs_s; /* Complexity Weight of 1 */\r\n", +" unsigned int shl; /* Complexity Weight of 1 */\r\n", +" unsigned int shr; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int extract_h; /* Complexity Weight of 1 */\r\n", +" unsigned int extract_l; /* Complexity Weight of 1 */\r\n", +" unsigned int mult; /* Complexity Weight of 1 */\r\n", +" unsigned int L_mult; /* Complexity Weight of 1 */\r\n", +" unsigned int negate; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int round; /* Complexity Weight of 1 */\r\n", +" unsigned int L_mac; /* Complexity Weight of 1 */\r\n", +" unsigned int L_msu; /* Complexity Weight of 1 */\r\n", +" unsigned int L_macNs; /* Complexity Weight of 1 */\r\n", +" unsigned int L_msuNs; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L_add; /* Complexity Weight of 1 */\r\n", +" unsigned int L_sub; /* Complexity Weight of 1 */\r\n", +" unsigned int L_add_c; /* Complexity Weight of 2 */\r\n", +" unsigned int L_sub_c; /* Complexity Weight of 2 */\r\n", +" unsigned int L_negate; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L_shl; /* Complexity Weight of 1 */\r\n", +" unsigned int L_shr; /* Complexity Weight of 1 */\r\n", +" unsigned int mult_r; /* Complexity Weight of 1 */\r\n", +" unsigned int shr_r; /* Complexity Weight of 3 */\r\n", +" unsigned int mac_r; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int msu_r; /* Complexity Weight of 1 */\r\n", +" unsigned int L_deposit_h; /* Complexity Weight of 1 */\r\n", +" unsigned int L_deposit_l; /* Complexity Weight of 1 */\r\n", +" unsigned int L_shr_r; /* Complexity Weight of 3 */\r\n", +" unsigned int L_abs; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L_sat; /* Complexity Weight of 4 */\r\n", +" unsigned int norm_s; /* Complexity Weight of 1 */\r\n", +" unsigned int div_s; /* Complexity Weight of 18 */\r\n", +" unsigned int norm_l; /* Complexity Weight of 1 */\r\n", +" unsigned int move16; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int move32; /* Complexity Weight of 2 */\r\n", +" unsigned int Logic16; /* Complexity Weight of 1 */\r\n", +" unsigned int Logic32; /* Complexity Weight of 2 */\r\n", +" unsigned int Test; /* Complexity Weight of 2 */\r\n", +" unsigned int s_max; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int s_min; /* Complexity Weight of 1 */\r\n", +" unsigned int L_max; /* Complexity Weight of 1 */\r\n", +" unsigned int L_min; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_max; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_min; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int shl_r; /* Complexity Weight of 3 */\r\n", +" unsigned int L_shl_r; /* Complexity Weight of 3 */\r\n", +" unsigned int L40_shr_r; /* Complexity Weight of 3 */\r\n", +" unsigned int L40_shl_r; /* Complexity Weight of 3 */\r\n", +" unsigned int norm_L40; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L40_shl; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_shr; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_negate; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_add; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_sub; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L40_abs; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_mult; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_mac; /* Complexity Weight of 1 */\r\n", +" unsigned int mac_r40; /* Complexity Weight of 2 */\r\n", +"\r\n", +" unsigned int L40_msu; /* Complexity Weight of 1 */\r\n", +" unsigned int msu_r40; /* Complexity Weight of 2 */\r\n", +" unsigned int Mpy_32_16_ss; /* Complexity Weight of 2 */\r\n", +" unsigned int Mpy_32_32_ss; /* Complexity Weight of 4 */\r\n", +" unsigned int L_mult0; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L_mac0; /* Complexity Weight of 1 */\r\n", +" unsigned int L_msu0; /* Complexity Weight of 1 */\r\n", +" unsigned int lshl; /* Complexity Weight of 1 */\r\n", +" unsigned int lshr; /* Complexity Weight of 1 */\r\n", +" unsigned int L_lshl; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L_lshr; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_lshl; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_lshr; /* Complexity Weight of 1 */\r\n", +" unsigned int s_and; /* Complexity Weight of 1 */\r\n", +" unsigned int s_or; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int s_xor; /* Complexity Weight of 1 */\r\n", +" unsigned int L_and; /* Complexity Weight of 1 */\r\n", +" unsigned int L_or; /* Complexity Weight of 1 */\r\n", +" unsigned int L_xor; /* Complexity Weight of 1 */\r\n", +" unsigned int rotl; /* Complexity Weight of 3 */\r\n", +"\r\n", +" unsigned int rotr; /* Complexity Weight of 3 */\r\n", +" unsigned int L_rotl; /* Complexity Weight of 3 */\r\n", +" unsigned int L_rotr; /* Complexity Weight of 3 */\r\n", +" unsigned int L40_set; /* Complexity Weight of 3 */\r\n", +" unsigned int L40_deposit_h; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L40_deposit_l; /* Complexity Weight of 1 */\r\n", +" unsigned int L40_deposit32; /* Complexity Weight of 1 */\r\n", +" unsigned int Extract40_H; /* Complexity Weight of 1 */\r\n", +" unsigned int Extract40_L; /* Complexity Weight of 1 */\r\n", +" unsigned int L_Extract40; /* Complexity Weight of 1 */\r\n", +"\r\n", +" unsigned int L40_round; /* Complexity Weight of 1 */\r\n", +" unsigned int L_saturate40; /* Complexity Weight of 1 */\r\n", +" unsigned int round40; /* Complexity Weight of 1 */\r\n", +" unsigned int If; /* Complexity Weight of 4 */\r\n", +" unsigned int Goto; /* Complexity Weight of 4 */\r\n", +"\r\n", +" unsigned int Break; /* Complexity Weight of 4 */\r\n", +" unsigned int Switch; /* Complexity Weight of 8 */\r\n", +" unsigned int For; /* Complexity Weight of 3 */\r\n", +" unsigned int While; /* Complexity Weight of 4 */\r\n", +" unsigned int Continue; /* Complexity Weight of 4 */\r\n", +"\r\n", +" unsigned int L_mls; /* Complexity Weight of 6 */\r\n", +" unsigned int div_l; /* Complexity Weight of 32 */\r\n", +" unsigned int i_mult; /* Complexity Weight of 3 */\r\n", +"} BASIC_OP;\r\n", +"\r\n", +"#ifdef WMOPS\r\n", +"extern BASIC_OP *multiCounter;\r\n", +"extern int currCounter;\r\n", +"\r\n", +"/* Technical note :\r\n", +" * The following 3 variables are only used for correct complexity\r\n", +" * evaluation of the following structure :\r\n", +" * IF{\r\n", +" * ...\r\n", +" * } ELSE IF {\r\n", +" * ...\r\n", +" * } ELSE IF {\r\n", +" * ...\r\n", +" * }\r\n", +" * ...\r\n", +" * } ELSE {\r\n", +" * ...\r\n", +" * }\r\n", +" */\r\n", +"extern int funcId_where_last_call_to_else_occurred;\r\n", +"extern long funcid_total_wmops_at_last_call_to_else;\r\n", +"extern int call_occurred;\r\n", +"\r\n", +"extern long TotalWeightedOperation( void );\r\n", +"long DeltaWeightedOperation( void );\r\n", +"\r\n", +"void Set_BASOP_WMOPS_counter( int counterId );\r\n", +"void Reset_BASOP_WMOPS_counter( void );\r\n", +"\r\n", +"#endif\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : FOR\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro FOR should be used instead of the 'for' C statement.\r\n", +" * The complexity is independent of the number of loop iterations that are\r\n", +" * performed.\r\n", +" *\r\n", +" * Complexity weight : 3 (regardless of number of iterations).\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define FOR( a) for( a)\r\n", +"\r\n", +"#else \r\n", +"#define FOR( a) if( incrFor(), 0); else for( a)\r\n", +"\r\n", +"static __inline void incrFor( void) {\r\n", +" multiCounter[currCounter].For++;\r\n", +"}\r\n", +"#endif \r\n", +"\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : WHILE\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro WHILE should be used instead of the 'while' C statement.\r\n", +" * The complexity is proportional to the number of loop iterations that\r\n", +" * are performed.\r\n", +" *\r\n", +" * Complexity weight : 4 x 'number of loop iterations'.\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define WHILE( a) while( a)\r\n", +"\r\n", +"#else \r\n", +"#define WHILE( a) while( incrWhile(), a)\r\n", +"\r\n", +"static __inline void incrWhile( void) {\r\n", +" multiCounter[currCounter].While++;\r\n", +"}\r\n", +"#endif \r\n", +"\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : DO\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro DO should be used instead of the 'do' C statement.\r\n", +" *\r\n", +" * Complexity weight : 0 (complexity counted by WHILE macro).\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define DO do\r\n", +"\r\n", +"#else \r\n", +"#define DO do\r\n", +"\r\n", +"#endif \r\n", +"\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : IF\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro IF should :\r\n", +" *\r\n", +" * - not be used when :\r\n", +" * - the 'if' structure does not have any 'else if' nor 'else' statement\r\n", +" * - and it conditions only one DSP basic operations.\r\n", +" *\r\n", +" * - be used instead of the 'if' C statement in every other case :\r\n", +" * - when there is an 'else' or 'else if' statement,\r\n", +" * - or when the 'if' conditions several DSP basic operations,\r\n", +" * - or when the 'if' conditions a function call.\r\n", +" *\r\n", +" * Complexity weight : 4\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define IF( a) if( a)\r\n", +"\r\n", +"#else \r\n", +"#define IF( a) if( incrIf(), a)\r\n", +"\r\n", +"static __inline void incrIf( void) {\r\n", +" /* Technical note :\r\n", +" * If the \"IF\" operator comes just after an \"ELSE\", its counter\r\n", +" * must not be incremented.\r\n", +" */\r\n", +" if ( ( currCounter != funcId_where_last_call_to_else_occurred ) || ( TotalWeightedOperation() != funcid_total_wmops_at_last_call_to_else ) || ( call_occurred == 1 ) )\r\n", +" {\r\n", +" multiCounter[currCounter].If++;\r\n", +" }\r\n", +"\r\n", +" call_occurred = 0;\r\n", +" funcId_where_last_call_to_else_occurred = INT_MAX;\r\n", +"}\r\n", +"#endif \r\n", +"\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : ELSE\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro ELSE should be used instead of the 'else' C statement.\r\n", +" *\r\n", +" * Complexity weight : 4\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define ELSE else\r\n", +"\r\n", +"#else \r\n", +"#define ELSE else if( incrElse(), 0) ; else\r\n", +"\r\n", +"static __inline void incrElse( void) {\r\n", +" multiCounter[currCounter].If++;\r\n", +"\r\n", +" /* We keep track of the funcId of the last function\r\n", +" * which used ELSE {...} structure.\r\n", +" */\r\n", +" funcId_where_last_call_to_else_occurred = currCounter;\r\n", +"\r\n", +" /* We keep track of the number of WMOPS of this funcId\r\n", +" * when the ELSE macro was called.\r\n", +" */\r\n", +" funcid_total_wmops_at_last_call_to_else = TotalWeightedOperation();\r\n", +"\r\n", +" /* call_occurred is set to 0, in order to count the next IF (if necessary)\r\n", +" */\r\n", +" call_occurred = 0;\r\n", +"}\r\n", +"#endif \r\n", +"\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : SWITCH\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro SWITCH should be used instead of the 'switch' C statement.\r\n", +" *\r\n", +" * Complexity weight : 8\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define SWITCH( a) switch( a)\r\n", +"\r\n", +"#else \r\n", +"#define SWITCH( a) switch( incrSwitch(), a)\r\n", +"\r\n", +"static __inline void incrSwitch( void) {\r\n", +" multiCounter[currCounter].Switch++;\r\n", +"}\r\n", +"#endif \r\n", +"\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : CONTINUE\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro CONTINUE should be used instead of the 'continue' C statement.\r\n", +" *\r\n", +" * Complexity weight : 4\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define CONTINUE continue\r\n", +"\r\n", +"#else \r\n", +"#define CONTINUE if( incrContinue(), 0); else continue\r\n", +"\r\n", +"static __inline void incrContinue( void) {\r\n", +" multiCounter[currCounter].Continue++;\r\n", +"}\r\n", +"#endif \r\n", +"\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : BREAK\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro BREAK should be used instead of the 'break' C statement.\r\n", +" *\r\n", +" * Complexity weight : 4\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define BREAK break\r\n", +"\r\n", +"#else \r\n", +"#define BREAK if( incrBreak(), 0) break; else break\r\n", +"\r\n", +"static __inline void incrBreak( void) {\r\n", +" multiCounter[currCounter].Break++;\r\n", +"}\r\n", +"#endif \r\n", +"\r\n", +"\r\n", +"/*****************************************************************************\r\n", +" *\r\n", +" * Function Name : GOTO\r\n", +" *\r\n", +" * Purpose :\r\n", +" *\r\n", +" * The macro GOTO should be used instead of the 'goto' C statement.\r\n", +" *\r\n", +" * Complexity weight : 4\r\n", +" *\r\n", +" *****************************************************************************/\r\n", +"#ifndef WMOPS\r\n", +"#define GOTO goto\r\n", +"\r\n", +"#else \r\n", +"#define GOTO if( incrGoto(), 0); else goto\r\n", +"\r\n", +"static __inline void incrGoto( void) {\r\n", +" multiCounter[currCounter].Goto++;\r\n", +"}\r\n", +"#endif \r\n", +"\r\n", +"#endif /* WMOPS_H */\r\n", +"\r\n", +"\r\n", +"\r\n", \ No newline at end of file diff --git a/src/wmc_tool/wmc_tool.cpp b/src/wmc_tool/wmc_tool.cpp index d0d7df2d..dac4c482 100644 --- a/src/wmc_tool/wmc_tool.cpp +++ b/src/wmc_tool/wmc_tool.cpp @@ -1028,23 +1028,44 @@ static TOOL_ERROR Output_Wmops_File( char *PathName, float frames_per_sec, bool { TOOL_ERROR ErrCode = NO_ERR; FILE *TargetFile; - int i, len; + int i, j, len, num_strings, offset; char SrcFileName[MAX_PATH + 1]; /* +1 for NUL Char*/ char TargetFileName[MAX_PATH + 1]; /* +1 for NUL Char*/ - char *text = NULL, *p_str, temp_str[50]; + char *text = NULL, temp_str[50]; const char* wmops_auto_files[] = { "wmc_auto.h", "wmc_auto.c" }; - static const char wmops_auto_file_h[] = -#include "wmc_auto_h.txt" - ; - static const char wmops_auto_file_c[] = -#include "wmc_auto_c.txt" - ; + const char** wmops_auto_file; + const char* wmops_auto_file_h[] = { + #include "wmc_auto_h.txt" + }; + const char* wmops_auto_file_c[] = { + #include "wmc_auto_c.txt" + }; for (i = 0; i < 2; i++) { - /* Allocate a char * buffer for further manipulation of the source file text */ - text = (char*)calloc(i == 0 ? sizeof(wmops_auto_file_h) : sizeof(wmops_auto_file_c), sizeof(char)); + /* get the pointer */ + if (i == 0) + { + wmops_auto_file = wmops_auto_file_h; + num_strings = sizeof(wmops_auto_file_h) / sizeof(char*); + } + else + { + wmops_auto_file = wmops_auto_file_c; + num_strings = sizeof(wmops_auto_file_c) / sizeof(char*); + } + + /* calculate the total length required */ + len = 0; + for (j = 0; j < num_strings; j++) + { + len += (int) strlen(wmops_auto_file[j]); + } + len++; + + /* allocate a char * buffer for further manipulation of the source file text */ + text = (char*) calloc(len, sizeof(char)); if (text == NULL) { ErrCode = ERR_FILE_READ; @@ -1052,16 +1073,22 @@ static TOOL_ERROR Output_Wmops_File( char *PathName, float frames_per_sec, bool goto ret; } - /* copy the source file contents to a char * buffer for further manipulation */ - strcpy(text, i == 0 ? wmops_auto_file_h : wmops_auto_file_c); - - /* Replace the constant FRAMES_PER_SECOND */ - p_str = strstr(text, "#define FRAMES_PER_SECOND"); - - if (p_str != NULL) + /* copy each string */ + offset = 0; + for (j = 0; j < num_strings; j++) { - sprintf(temp_str, "#define FRAMES_PER_SECOND %-8.1f", frames_per_sec); - strncpy(p_str, temp_str, strlen(temp_str)); + if (strstr(wmops_auto_file[j], "#define FRAMES_PER_SECOND") != NULL) + { + /* replace the declaration of FRAMES_PER_SECOND with user-defined value */ + sprintf(temp_str, "#define FRAMES_PER_SECOND %-8.1f", frames_per_sec); + strcpy(text + offset, temp_str); + offset += (int) strlen(temp_str); + } + else + { + strcpy(text + offset, wmops_auto_file[j]); + offset += (int) strlen(wmops_auto_file[j]); + } } /* Create Target Filename */ From 902094949289fe9e4806a8ada3969c480d158f45 Mon Sep 17 00:00:00 2001 From: Ludovic Malfait Date: Tue, 16 Apr 2024 15:21:43 +0100 Subject: [PATCH 52/53] Manual and recommendation updates for STL2024 --- doc/draft_g191.docx | Bin 0 -> 36996 bytes doc/g191.md | 24 +++++++++++++++++++----- doc/manual/STLmanual.tex | 6 +++--- doc/manual/intro.tex | 6 ++++++ 4 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 doc/draft_g191.docx diff --git a/doc/draft_g191.docx b/doc/draft_g191.docx new file mode 100644 index 0000000000000000000000000000000000000000..9015f4cee9975db36bf036d637d15fd251de0c84 GIT binary patch literal 36996 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

~wIX6oJn>>UgR)I_h!9fS>R5aM*_W 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 z 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 literal 0 HcmV?d00001 diff --git a/doc/g191.md b/doc/g191.md index 0feabbbb..8b3802f0 100644 --- a/doc/g191.md +++ b/doc/g191.md @@ -12,17 +12,31 @@ Please refer to the official Recommendation ITU-T G.191 for the complete text: [ Recommendation ITU-T G.191 provides source code for speech and audio processing modules for narrowband, wideband and super-wideband telephony applications. The set includes codecs, filters, noise generators. -This edition introduces changes to Annex A, which describes the ITU-T Software Tools (STL) containing a high-quality, portable C code library for speech processing applications. This release of the STL, also known as STL2023, incorporates: +This edition introduces changes to Annex A, which describes the ITU-T Software Tools (STL) containing a high-quality, portable C code library for speech processing applications. This release of the STL, also known as STL2024, incorporates: -* An implementation of P.50 fullband MNRU as described in ITU-T P.810. +* BS1770demo improvements: -* A tool for automatic instrumentation of speech and audio codecs to measure their computational complexity and memory. + - a new RMS option disabling the gating fuction for background noise measurement, -Recommendation ITU-T G.191 includes an electronic attachment containing STL2023 and manual. + - the handling of the edge case where all gating blacks are below -70 LKFS, + + - improved reporting with scaling factor being reported in the linear and log domains. + +* WMC Tools updates: + + - New command line parameter to allow control on the number of frames per second (default still 50) + + - Export information on all memory allocations occuring during runtime + + - Example script for graphical analysis and profiling of dymanic memory allocation + + - Bugs fixes and code improvements + +Recommendation ITU-T G.191 includes an electronic attachment containing STL2024 and manual. ## Keywords -DSP operators, filters, MNRU, P.50 FB MNRU, open source, reverb, STL2022, G.711, G.722, G.726, G.728, sv56, BS.1770, ESDRU, WMC Tool +DSP operators, filters, MNRU, P.50 FB MNRU, open source, reverb, G.711, G.722, G.726, G.728, sv56, BS.1770, ESDRU, WMC Tool ## 1. Scope diff --git a/doc/manual/STLmanual.tex b/doc/manual/STLmanual.tex index 58185b8c..a9cc935a 100644 --- a/doc/manual/STLmanual.tex +++ b/doc/manual/STLmanual.tex @@ -18,7 +18,7 @@ \addtolength{\itemsep}{-20pt} % Define headers -\def\ugst_title{ ITU-T Software Tool Library, release 2023} +\def\ugst_title{ ITU-T Software Tool Library, release 2024} \def\us{$\mu$s} \markboth{ \hspace{1cm} \hfill \ugst_title }% { Version: \today \hfill \hspace{1cm} } @@ -61,7 +61,7 @@ \pagenumbering{roman} %============================================================================== -\title{ITU-T Software Tool Library 2023 User's Manual} +\title{ITU-T Software Tool Library 2024 User's Manual} \author{ITU-T Users' Group on Software Tools} %------------------------------------------------------------------------------ @@ -71,7 +71,7 @@ \ruley{100mm} - Copyright \copyright~ 2005, 2006, 2009, 2019, 2022 and 2023 by the International + Copyright \copyright~ 2005, 2006, 2009, 2019, 2022, 2023 and 2024 by the International Telecommunication Union (ITU) \ruley{15mm} diff --git a/doc/manual/intro.tex b/doc/manual/intro.tex index ae1c10b4..07eadbe2 100644 --- a/doc/manual/intro.tex +++ b/doc/manual/intro.tex @@ -29,6 +29,8 @@ \chapter{Introduction} In 2023, STL release incorporates an implementation of P.50 fullband MNRU as described in ITU-T Rec. P.810, as well as WMC tool, a tool for automatic instrumentation of speech and audio codecs to measure their computational complexity and memory. +In 2024, the revision of the STL offers additional features and bug fixes to the BS.1770 demo and WMC tool. + Since STL2019, the build toolchain uses CMake to generate platform-dependent and tool-dependent build scripts as well as to execute regression tests for each module in the STL. Modules have been tested on Windows, MacOS and several Linux flavors. @@ -191,6 +193,8 @@ \section{Acknowledgements} For the STL2023, the implementation of the P.50 Fullband MNRU was kindly provided by Mrs Anna Llagostera and Mr Jens Berger from SwissQual AG/Rohde \& Schwarz, with a contribution from NTT on filter routines, the integration into the existing MNRU code and documentation from Ludovic Malfait, and the DC filter and amplitude clipping improvement suggestions from Anssi Ramo (Nokia). The implementation of the WMC tool was kindly provided by Mr Guy Richard and Mr Vladimir Malenovsky from VoiceAge Corporation. +For the STL2024, the improvements to BS.1770 demo and WMC Tool were kindly provided by Mr Erik Norvell from Ericsson and Mr Vladimir Malenovsky from VoiceAge Corporation respectively. + Above all, special thank goes to ITU-T SG16 Counselor Mr~Sim\~ao Ferraz de Campos Neto, the ``father'' of the STL. %============================================================================= @@ -230,6 +234,8 @@ \chapter{Tutorial} \item[STL2009] ITU-T Software Tools Library, release 2009. \item[STL2019] ITU-T Software Tools Library, release 2019. \item[STL2022] ITU-T Software Tools Library, release 2022. +\item[STL2023] ITU-T Software Tools Library, release 2023. +\item[STL2024] ITU-T Software Tools Library, release 2024. \item[UGST] Users' Group on Software Tools, of ITU-T Study Group 16. \end{Descr} From ac339cc62c85510f5f8d815f0d42899431c359ec Mon Sep 17 00:00:00 2001 From: Ludovic Malfait Date: Tue, 16 Apr 2024 15:29:01 +0100 Subject: [PATCH 53/53] Minor update --- doc/g191.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/g191.md b/doc/g191.md index 8b3802f0..58cab3b7 100644 --- a/doc/g191.md +++ b/doc/g191.md @@ -16,17 +16,17 @@ This edition introduces changes to Annex A, which describes the ITU-T Software T * BS1770demo improvements: - - a new RMS option disabling the gating fuction for background noise measurement, + - a new RMS option disabling the gating function for background noise measurement, - the handling of the edge case where all gating blacks are below -70 LKFS, - improved reporting with scaling factor being reported in the linear and log domains. -* WMC Tools updates: +* WMC Tool updates: - New command line parameter to allow control on the number of frames per second (default still 50) - - Export information on all memory allocations occuring during runtime + - Export information on all memory allocations occurring during runtime - Example script for graphical analysis and profiling of dymanic memory allocation