1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2005-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%%---------------------------------------------------------------------- 23%% Purpose: mstone measurement 24%% 25%%---------------------------------------------------------------------- 26 27-module(megaco_codec_mstone1). 28 29 30%% API 31-export([ 32 start/0, start/1, start/2, 33 start_flex/0, start_flex/1, start_flex/2, 34 start_no_drv/0, start_no_drv/1, start_no_drv/2, 35 start_only_drv/0, start_only_drv/1, start_only_drv/2 36 ]). 37 38%% Internal exports 39-export([mstone_runner_init/6]). 40 41 42-define(LIB, megaco_codec_mstone_lib). 43 44-ifndef(MSTONE_RUN_TIME). 45-define(MSTONE_RUN_TIME, 10). % minutes 46-endif. 47 48-ifndef(MSTONE_VERSION3). 49-define(MSTONE_VERSION3, v3). 50-endif. 51-define(VERSION3, ?MSTONE_VERSION3). 52 53-ifndef(MSTONE_CODECS). 54-define(MSTONE_CODECS, megaco_codec_transform:codecs()). 55-endif. 56 57-define(DEFAULT_MESSAGE_PACKAGE, megaco_codec_transform:default_message_package()). 58-define(DEFAULT_FACTOR, 1). 59-define(DEFAULT_DRV_INCLUDE, ignore). 60 61%% -define(VERBOSE_STATS,true). 62 63-ifndef(MSTONE_RUNNER_MIN_HEAP_SZ). 64-define(MSTONE_RUNNER_MIN_HEAP_SZ, 16#ffff). 65-endif. 66-define(MSTONE_RUNNER_OPTS, 67 [link, {min_heap_size, ?MSTONE_RUNNER_MIN_HEAP_SZ}]). 68 69-record(mstone, {id, count, codec, econf, heap_size, reds}). 70 71 72start() -> 73 start(?DEFAULT_FACTOR). 74 75start([Factor]) -> 76 start(?DEFAULT_MESSAGE_PACKAGE, ?MSTONE_RUN_TIME, Factor); 77start([MessagePackage, Factor]) -> 78 start(MessagePackage, ?MSTONE_RUN_TIME, Factor); 79start([MessagePackage, RunTime, Factor]) -> 80 start(MessagePackage, RunTime, Factor); 81start(Factor) -> 82 start(?DEFAULT_MESSAGE_PACKAGE, ?MSTONE_RUN_TIME, Factor). 83 84start(MessagePackage, Factor) -> 85 start(MessagePackage, ?MSTONE_RUN_TIME, Factor). 86 87start(MessagePackage, RunTime, Factor) -> 88 do_start(MessagePackage, RunTime, Factor, ?DEFAULT_DRV_INCLUDE). 89 90 91start_flex() -> 92 start_flex(?DEFAULT_FACTOR). 93 94start_flex([Factor]) -> 95 start_flex(?DEFAULT_MESSAGE_PACKAGE, ?MSTONE_RUN_TIME, Factor); 96start_flex([MessagePackage, Factor]) -> 97 start_flex(MessagePackage, ?MSTONE_RUN_TIME, Factor); 98start_flex([MessagePackage, RunTime, Factor]) -> 99 start_flex(MessagePackage, RunTime, Factor); 100start_flex(Factor) -> 101 start_flex(?DEFAULT_MESSAGE_PACKAGE, ?MSTONE_RUN_TIME, Factor). 102 103start_flex(MessagePackage, Factor) -> 104 do_start(MessagePackage, ?MSTONE_RUN_TIME, Factor, flex). 105 106start_flex(MessagePackage, RunTime, Factor) -> 107 do_start(MessagePackage, RunTime, Factor, flex). 108 109 110start_only_drv() -> 111 start_only_drv(?DEFAULT_FACTOR). 112 113start_only_drv([Factor]) -> 114 start_only_drv(?DEFAULT_MESSAGE_PACKAGE, ?MSTONE_RUN_TIME, Factor); 115start_only_drv([MessagePackage, Factor]) -> 116 start_only_drv(MessagePackage, ?MSTONE_RUN_TIME, Factor); 117start_only_drv([MessagePackage, RunTime, Factor]) -> 118 start_only_drv(MessagePackage, RunTime, Factor); 119start_only_drv(Factor) -> 120 start_only_drv(?DEFAULT_MESSAGE_PACKAGE, ?MSTONE_RUN_TIME, Factor). 121 122start_only_drv(MessagePackage, Factor) -> 123 do_start(MessagePackage, ?MSTONE_RUN_TIME, Factor, only_drv). 124 125start_only_drv(MessagePackage, RunTime, Factor) -> 126 do_start(MessagePackage, RunTime, Factor, only_drv). 127 128 129start_no_drv() -> 130 start_no_drv(?DEFAULT_FACTOR). 131 132start_no_drv([Factor]) -> 133 start_no_drv(?DEFAULT_MESSAGE_PACKAGE, ?MSTONE_RUN_TIME, Factor); 134start_no_drv([MessagePackage, Factor]) -> 135 start_no_drv(MessagePackage, ?MSTONE_RUN_TIME, Factor); 136start_no_drv([MessagePackage, RunTime, Factor]) -> 137 start_no_drv(MessagePackage, RunTime, Factor); 138start_no_drv(Factor) -> 139 start_no_drv(?DEFAULT_MESSAGE_PACKAGE, ?MSTONE_RUN_TIME, Factor). 140 141start_no_drv(MessagePackage, Factor) -> 142 do_start(MessagePackage, ?MSTONE_RUN_TIME, Factor, no_drv). 143 144start_no_drv(MessagePackage, RunTime, Factor) -> 145 do_start(MessagePackage, RunTime, Factor, no_drv). 146 147 148do_start(MessagePackageRaw, RunTimeRaw, FactorRaw, DrvInclude) -> 149 RunTime = parse_runtime(RunTimeRaw), 150 Factor = parse_factor(FactorRaw), 151 MessagePackage = parse_message_package(MessagePackageRaw), 152 mstone_init(MessagePackage, RunTime, Factor, DrvInclude). 153 154 155parse_runtime(RunTimeAtom) when is_atom(RunTimeAtom) -> 156 parse_runtime_str(atom_to_list(RunTimeAtom)); 157parse_runtime(RunTimeStr) when is_list(RunTimeStr) -> 158 parse_runtime_str(RunTimeStr); 159parse_runtime(RunTime) when is_integer(RunTime) andalso (RunTime > 0) -> 160 timer:minutes(RunTime); 161parse_runtime(BadRunTime) -> 162 throw({error, {bad_runtime, BadRunTime}}). 163 164parse_runtime_str(RuneTimeStr) -> 165 try 166 begin 167 case lists:reverse(RuneTimeStr) of 168 [$s|Rest] -> 169 timer:seconds(list_to_integer(lists:reverse(Rest))); 170 [$m|Rest] -> 171 timer:minutes(list_to_integer(lists:reverse(Rest))); 172 [$h|Rest] -> 173 timer:hours(list_to_integer(lists:reverse(Rest))); 174 _ -> 175 timer:minutes(list_to_integer(RuneTimeStr)) 176 end 177 end 178 catch 179 _:_:_ -> 180 throw({error, {bad_runtime, RuneTimeStr}}) 181 end. 182 183 184parse_factor(FactorAtom) when is_atom(FactorAtom) -> 185 case (catch list_to_integer(atom_to_list(FactorAtom))) of 186 Factor when is_integer(Factor) andalso (Factor > 0) -> 187 Factor; 188 _ -> 189 io:format("ERROR: Bad factor value: ~p~n", [FactorAtom]), 190 throw({error, {bad_factor, FactorAtom}}) 191 end; 192parse_factor(FactorRaw) when is_list(FactorRaw) -> 193 case (catch list_to_integer(FactorRaw)) of 194 Factor when is_integer(Factor) andalso (Factor > 0) -> 195 Factor; 196 _ -> 197 io:format("ERROR: Bad factor value: ~p~n", [FactorRaw]), 198 throw({error, {bad_factor, FactorRaw}}) 199 end; 200parse_factor(Factor) when is_integer(Factor) andalso (Factor > 0) -> 201 Factor; 202parse_factor(BadFactor) -> 203 throw({error, {bad_factor, BadFactor}}). 204 205 206parse_message_package(MessagePackageRaw) when is_list(MessagePackageRaw) -> 207 list_to_atom(MessagePackageRaw); 208parse_message_package(MessagePackage) when is_atom(MessagePackage) -> 209 MessagePackage; 210parse_message_package(BadMessagePackage) -> 211 throw({error, {bad_message_package, BadMessagePackage}}). 212 213 214%% Codecs is a list of megaco codec shortnames: 215%% 216%% pretty | compact | ber | per | erlang 217%% 218 219mstone_init(MessagePackage, RunTime, Factor, DrvInclude) -> 220%% io:format("mstone_init -> entry with" 221%% "~n MessagePackage: ~p" 222%% "~n RunTime: ~p" 223%% "~n Factor: ~p" 224%% "~n DrvInclude: ~p" 225%% "~n", [MessagePackage, RunTime, Factor, DrvInclude]), 226 Codecs = ?MSTONE_CODECS, 227 mstone_init(MessagePackage, RunTime, Factor, Codecs, DrvInclude). 228 229mstone_init(MessagePackage, RunTime, Factor, Codecs, DrvInclude) -> 230 Parent = self(), 231 Pid = spawn( 232 fun() -> 233 process_flag(trap_exit, true), 234 do_mstone(MessagePackage, 235 RunTime, Factor, Codecs, DrvInclude), 236 Parent ! {done, self()} 237 end), 238 receive 239 {done, Pid} -> 240 ok 241 end. 242 243do_mstone(MessagePackage, RunTime, Factor, Codecs, DrvInclude) -> 244 io:format("~n", []), 245 ?LIB:display_os_info(), 246 ?LIB:display_system_info(), 247 ?LIB:display_app_info(), 248 io:format("~n", []), 249 (catch asn1rt_driver_handler:load_driver()), 250 {Pid, Conf} = ?LIB:start_flex_scanner(), 251 put(flex_scanner_conf, Conf), 252 EMessages = ?LIB:expanded_messages(MessagePackage, Codecs, DrvInclude), 253 EMsgs = duplicate(Factor, EMessages), 254 MStone = t1(RunTime, EMsgs), 255 ?LIB:stop_flex_scanner(Pid), 256 io:format("~n", []), 257 io:format("MStone: ~p~n", [MStone]). 258 259duplicate(N, Elements) -> 260 duplicate(N, Elements, []). 261 262duplicate(_N, [], Acc) -> 263 lists:flatten(Acc); 264duplicate(N, [H|T], Acc) -> 265 duplicate(N, T, [lists:duplicate(N, H)|Acc]). 266 267t1(RunTime, EMsgs) -> 268 io:format(" * starting runners [~w] ", [length(EMsgs)]), 269 t1(RunTime, EMsgs, []). 270 271t1(_RunTime, [], Runners) -> 272 io:format(" done~n * await runners ready ", []), 273 await_runners_ready(Runners), 274 io:format(" done~n * now snooze", []), 275 receive after 5000 -> ok end, 276 io:format("~n * release them~n", []), 277 lists:foreach(fun(P) -> P ! {go, self()} end, Runners), 278 t2(1, [], Runners); 279t1(RunTime, [H|T], Runners) -> 280 Runner = init_runner(RunTime, H), 281 io:format(".", []), 282 t1(RunTime, T, [Runner|Runners]). 283 284await_runners_ready([]) -> 285 ok; 286await_runners_ready(Runners) -> 287 receive 288 {ready, Runner} -> 289 io:format(".", []), 290 %% i("runner ~w ready", [Runner]), 291 await_runners_ready(lists:delete(Runner, Runners)); 292 {'EXIT', Pid, Reason} -> 293 case lists:member(Pid, Runners) of 294 true -> 295 io:format("~nERROR: " 296 "received (unexpected) exit signal " 297 "from from runner ~p:" 298 "~n~p~n", [Pid, Reason]), 299 exit(Reason); 300 false -> 301 await_runners_ready(Runners) 302 end 303 end. 304 305-ifdef(VERBOSE_STATS). 306print_runner_stats(RunnerStats) -> 307 Sorted = lists:keysort(2, RunnerStats), 308 lists:foreach(fun(#mstone{id = Id, 309 count = Num, 310 codec = Codec, 311 econf = EConf, 312 heap_size = HeapSz, 313 reds = Reds}) -> 314 i("runner: ~w" 315 "~n Count: ~w" 316 "~n Codec: ~w" 317 "~n Encoding config: ~p" 318 "~n Heap size: ~p" 319 "~n Reductions: ~p", 320 [Id, Num, Codec, EConf, HeapSz, Reds]) end, 321 Sorted), 322 ok. 323-else. 324print_runner_stats(_) -> 325 ok. 326-endif. 327 328t2(_, Acc, []) -> 329 i("~n~w runners", [length(Acc)]), 330 print_runner_stats(Acc), 331 332 HeapSzAcc = lists:sort([HS || #mstone{heap_size = HS} <- Acc]), 333 i("Runner heap size data:" 334 "~n Min: ~w" 335 "~n Max: ~w" 336 "~n Avg: ~w", 337 [hd(HeapSzAcc), 338 hd(lists:reverse(HeapSzAcc)), 339 (lists:sum(HeapSzAcc) div length(HeapSzAcc))]), 340 341 RedsAcc = lists:sort([R || #mstone{reds = R} <- Acc]), 342 i("Runner reductions data:" 343 "~n Min: ~w" 344 "~n Max: ~w" 345 "~n Avg: ~w", 346 [hd(RedsAcc), 347 hd(lists:reverse(RedsAcc)), 348 (lists:sum(RedsAcc) div length(RedsAcc))]), 349 350 lists:sum([Num || #mstone{count = Num} <- Acc]); 351t2(N, Acc, Runners) -> 352 receive 353 {'EXIT', Pid, {runner_done, Codec, Conf, Num, Info}} -> 354 {value, {_, HeapSz}} = lists:keysearch(heap_size, 1, Info), 355 {value, {_, Reds}} = lists:keysearch(reductions, 1, Info), 356 MStone = #mstone{id = N, 357 count = Num, 358 codec = Codec, 359 econf = Conf, 360 heap_size = HeapSz, 361 reds = Reds}, 362 t2(N + 1, [MStone|Acc], lists:delete(Pid, Runners)) 363 end. 364 365init_runner(RunTime, {Codec, Mod, Conf, Msgs}) -> 366 Conf1 = runner_conf(Conf), 367 Conf2 = [{version3,?VERSION3}|Conf1], 368 Pid = spawn_opt(?MODULE, mstone_runner_init, 369 [RunTime, Codec, self(), Mod, Conf2, Msgs], 370 ?MSTONE_RUNNER_OPTS), 371 Pid. 372 373runner_conf([flex_scanner]) -> 374 get(flex_scanner_conf); 375runner_conf(Conf) -> 376 Conf. 377 378 379 380detect_versions(Codec, _Conf, [], []) -> 381 exit({no_messages_found_for_codec, Codec}); 382detect_versions(_Codec, _Conf, [], Acc) -> 383 lists:reverse(Acc); 384detect_versions(Codec, Conf, [{_Name, Bin}|Bins], Acc) -> 385 Data = ?LIB:detect_version(Codec, Conf, Bin), 386 detect_versions(Codec, Conf, Bins, [Data|Acc]). 387 388 389mstone_runner_init(RunTime, _Codec, Parent, Mod, Conf, Msgs0) -> 390 Msgs = detect_versions(Mod, Conf, Msgs0, []), 391 warmup(Mod, Conf, Msgs, []), 392 Parent ! {ready, self()}, 393 receive 394 {go, Parent} -> 395 ok 396 end, 397 erlang:send_after(RunTime, self(), stop), 398 mstone_runner_loop(Parent, Mod, Conf, 0, Msgs). 399 400mstone_runner_loop(Parent, Mod, Conf, N, Msgs1) -> 401 receive 402 stop -> 403 exit({runner_done, Mod, Conf, N, mstone_runner_process_info()}) 404 after 0 -> 405 {Inc, Msgs2} = mstone_all(Mod, Conf, Msgs1, []), 406 mstone_runner_loop(Parent, Mod, Conf, N+Inc, Msgs2) 407 end. 408 409mstone_runner_process_info() -> 410 PI = process_info(self()), 411 FL = [heap_size, stack_size, reductions], 412 lists:filter(fun({Key, _}) -> lists:member(Key, FL) end, PI). 413 414 415mstone_all(_Codec, _Conf, [], Acc) -> 416 {length(Acc), lists:reverse(Acc)}; 417mstone_all(Codec, Conf, [{V, Bin}|Bins], Acc) when is_binary(Bin) -> 418 {ok, Msg} = apply(Codec, decode_message, [Conf, V, Bin]), 419 mstone_all(Codec, Conf, Bins, [{V, Msg}|Acc]); 420mstone_all(Codec, Conf, [{V, Msg}|Msgs], Acc) -> 421 {ok, Bin} = apply(Codec, encode_message, [Conf, V, Msg]), 422 mstone_all(Codec, Conf, Msgs, [{V, Bin}|Acc]). 423 424warmup(_Codec, _Conf, [], Acc) -> 425 lists:reverse(Acc); 426warmup(Codec, Conf, [{V, M}|Msgs], Acc) -> 427%% io:format("~p warmup -> entry with" 428%% "~n Codec: ~p" 429%% "~n Conf: ~p" 430%% "~n", [self(), Codec, Conf]), 431 case (catch apply(Codec, decode_message, [Conf, V, M])) of 432 {ok, Msg} -> 433 case (catch apply(Codec, encode_message, [Conf, V, Msg])) of 434 {ok, Bin} -> 435 warmup(Codec, Conf, Msgs, [Bin|Acc]); 436 EncodeError -> 437 emsg("failed encoding message: ~n~p", [EncodeError]) 438 end; 439 DecodeError -> 440 emsg("failed decoding message: " 441 "~n DecodeError: ~p" 442 "~n V: ~p" 443 "~n M: ~p", [DecodeError, V, M]) 444 end. 445 446 447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 448 449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 450 451emsg(F, A) -> 452 error_logger:error_msg(F ++ "~n", A). 453 454%% i(F) -> 455%% i(F, []). 456i(F, A) -> 457 io:format(F ++ "~n", A). 458 459