From 666ab5691f8d8f9f33b0887cfaeea6d3a9067646 Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Fri, 29 Nov 2024 17:55:11 +0800 Subject: [PATCH] =?UTF-8?q?ft=EF=BC=9A=E5=88=A0=E9=99=A4=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/recon-2.5.1/fmtTest.erl | 22 - src/test/recon-2.5.1/recon.erl | 713 ------------------------ src/test/recon-2.5.1/recon_alloc.erl | 778 --------------------------- src/test/recon-2.5.1/recon_lib.erl | 285 ---------- src/test/recon-2.5.1/recon_map.erl | 208 ------- src/test/recon-2.5.1/recon_rec.erl | 279 ---------- src/test/recon-2.5.1/recon_test.erl | 539 ------------------- src/test/recon-2.5.1/recon_trace.erl | 733 ------------------------- src/test/recon-2.5.1/utTc.erl | 184 ------- 9 files changed, 3741 deletions(-) delete mode 100644 src/test/recon-2.5.1/fmtTest.erl delete mode 100644 src/test/recon-2.5.1/recon.erl delete mode 100644 src/test/recon-2.5.1/recon_alloc.erl delete mode 100644 src/test/recon-2.5.1/recon_lib.erl delete mode 100644 src/test/recon-2.5.1/recon_map.erl delete mode 100644 src/test/recon-2.5.1/recon_rec.erl delete mode 100644 src/test/recon-2.5.1/recon_test.erl delete mode 100644 src/test/recon-2.5.1/recon_trace.erl delete mode 100644 src/test/recon-2.5.1/utTc.erl diff --git a/src/test/recon-2.5.1/fmtTest.erl b/src/test/recon-2.5.1/fmtTest.erl deleted file mode 100644 index e43ed84..0000000 --- a/src/test/recon-2.5.1/fmtTest.erl +++ /dev/null @@ -1,22 +0,0 @@ --module(fmtTest). - --compile([export_all, nowarn_export_all]). - -tt1(FmtBinStr) -> - binary:split(FmtBinStr, <<"~">>). - -tt2(FmtBinStr) -> - binary:split(FmtBinStr, persistent_term:get(eFmtPtMc)). - -tt5(F) -> - string:split(F, "~"). - -tt3(<<>>) -> - ok; -tt3(<<_F/utf8, L/binary>>) -> - tt3(L). - -tt4([]) -> - ok; -tt4([_F | L]) -> - tt4(L). \ No newline at end of file diff --git a/src/test/recon-2.5.1/recon.erl b/src/test/recon-2.5.1/recon.erl deleted file mode 100644 index 6848e4b..0000000 --- a/src/test/recon-2.5.1/recon.erl +++ /dev/null @@ -1,713 +0,0 @@ -%%% @author Fred Hebert -%%% [http://ferd.ca/] -%%% @doc Recon, as a module, provides access to the high-level functionality -%%% contained in the Recon application. -%%% -%%% It has functions in five main categories: -%%% -%%%
-%%%
1. State information
-%%%
Process information is everything that has to do with the -%%% general state of the node. Functions such as {@link info/1} -%%% and {@link info/3} are wrappers to provide more details than -%%% `erlang:process_info/1', while providing it in a production-safe -%%% manner. They have equivalents to `erlang:process_info/2' in -%%% the functions {@link info/2} and {@link info/4}, respectively.
-%%%
{@link proc_count/2} and {@link proc_window/3} are to be used -%%% when you require information about processes in a larger sense: -%%% biggest consumers of given process information (say memory or -%%% reductions), either absolutely or over a sliding time window, -%%% respectively.
-%%%
{@link bin_leak/1} is a function that can be used to try and -%%% see if your Erlang node is leaking refc binaries. See the function -%%% itself for more details.
-%%%
Functions to access node statistics, in a manner somewhat similar -%%% to what vmstats -%%% provides as a library. There are 3 of them: -%%% {@link node_stats_print/2}, which displays them, -%%% {@link node_stats_list/2}, which returns them in a list, and -%%% {@link node_stats/4}, which provides a fold-like interface -%%% for stats gathering. For CPU usage specifically, see -%%% {@link scheduler_usage/1}.
-%%% -%%%
2. OTP tools
-%%%
This category provides tools to interact with pieces of OTP -%%% more easily. At this point, the only function included is -%%% {@link get_state/1}, which works as a wrapper around -%%% {@link get_state/2}, which works as a wrapper around -%%% `sys:get_state/1' in R16B01, and provides the required -%%% functionality for older versions of Erlang.
-%%% -%%%
3. Code Handling
-%%%
Specific functions are in `recon' for the sole purpose -%%% of interacting with source and compiled code. -%%% {@link remote_load/1} and {@link remote_load/2} will allow -%%% to take a local module, and load it remotely (in a diskless -%%% manner) on another Erlang node you're connected to.
-%%%
{@link source/1} allows to print the source of a loaded module, -%%% in case it's not available in the currently running node.
-%%% -%%%
4. Ports and Sockets
-%%%
To make it simpler to debug some network-related issues, -%%% recon contains functions to deal with Erlang ports (raw, file -%%% handles, or inet). Functions {@link tcp/0}, {@link udp/0}, -%%% {@link sctp/0}, {@link files/0}, and {@link port_types/0} will -%%% list all the Erlang ports of a given type. The latter function -%%% prints counts of all individual types.
-%%%
Port state information can be useful to figure out why certain -%%% parts of the system misbehave. Functions such as -%%% {@link port_info/1} and {@link port_info/2} are wrappers to provide -%%% more similar or more details than `erlang:port_info/1-2', and, for -%%% inet ports, statistics and options for each socket.
-%%%
Finally, the functions {@link inet_count/2} and {@link inet_window/3} -%%% provide the absolute or sliding window functionality of -%%% {@link proc_count/2} and {@link proc_count/3} to inet ports -%%% and connections currently on the node.
-%%% -%%%
5. RPC
-%%%
These are wrappers to make RPC work simpler with clusters of -%%% Erlang nodes. Default RPC mechanisms (from the `rpc' module) -%%% make it somewhat painful to call shell-defined funs over node -%%% boundaries. The functions {@link rpc/1}, {@link rpc/2}, and -%%% {@link rpc/3} will do it with a simpler interface.
-%%%
Additionally, when you're running diagnostic code on remote -%%% nodes and want to know which node evaluated what result, using -%%% {@link named_rpc/1}, {@link named_rpc/2}, and {@link named_rpc/3} -%%% will wrap the results in a tuple that tells you which node it's -%%% coming from, making it easier to identify bad nodes.
-%%%
-%%% @end --module(recon). --export([info/1, info/2, info/3, info/4, - proc_count/2, proc_window/3, - bin_leak/1, - node_stats_print/2, node_stats_list/2, node_stats/4, - scheduler_usage/1]). --export([get_state/1, get_state/2]). --export([remote_load/1, remote_load/2, - source/1]). --export([tcp/0, udp/0, sctp/0, files/0, port_types/0, - inet_count/2, inet_window/3, - port_info/1, port_info/2]). --export([rpc/1, rpc/2, rpc/3, - named_rpc/1, named_rpc/2, named_rpc/3]). - -%%%%%%%%%%%%% -%%% TYPES %%% -%%%%%%%%%%%%% --type proc_attrs() :: {pid(), - Attr :: _, - [Name :: atom() - |{current_function, mfa()} - |{initial_call, mfa()}, ...]}. - --type inet_attrs() :: {port(), - Attr :: _, - [{atom(), term()}]}. - --type pid_term() :: pid() | atom() | string() -| {global, term()} | {via, module(), term()} -| {non_neg_integer(), non_neg_integer(), non_neg_integer()}. - --type info_type() :: meta | signals | location | memory_used | work. - --type info_meta_key() :: registered_name | dictionary | group_leader | status. --type info_signals_key() :: links | monitors | monitored_by | trap_exit. --type info_location_key() :: initial_call | current_stacktrace. --type info_memory_key() :: memory | message_queue_len | heap_size -| total_heap_size | garbage_collection. --type info_work_key() :: reductions. - --type info_key() :: info_meta_key() | info_signals_key() | info_location_key() -| info_memory_key() | info_work_key(). - --type port_term() :: port() | string() | atom() | pos_integer(). - --type port_info_type() :: meta | signals | io | memory_used | specific. - --type port_info_meta_key() :: registered_name | id | name | os_pid. --type port_info_signals_key() :: connected | links | monitors. --type port_info_io_key() :: input | output. --type port_info_memory_key() :: memory | queue_size. --type port_info_specific_key() :: atom(). - --type port_info_key() :: port_info_meta_key() | port_info_signals_key() -| port_info_io_key() | port_info_memory_key() -| port_info_specific_key(). - --export_type([proc_attrs/0, inet_attrs/0, pid_term/0, port_term/0]). --export_type([info_type/0, info_key/0, - info_meta_key/0, info_signals_key/0, info_location_key/0, - info_memory_key/0, info_work_key/0]). --export_type([port_info_type/0, port_info_key/0, - port_info_meta_key/0, port_info_signals_key/0, port_info_io_key/0, - port_info_memory_key/0, port_info_specific_key/0]). - -%%%%%%%%%%%%%%%%%% -%%% PUBLIC API %%% -%%%%%%%%%%%%%%%%%% - -%%% Process Info %%% - -%% @doc Equivalent to `info()' where `A', `B', and `C' are integers part -%% of a pid --spec info(N, N, N) -> [{info_type(), [{info_key(), term()}]}, ...] when - N :: non_neg_integer(). -info(A, B, C) -> info(recon_lib:triple_to_pid(A, B, C)). - -%% @doc Equivalent to `info(, Key)' where `A', `B', and `C' are integers part -%% of a pid --spec info(N, N, N, Key) -> term() when - N :: non_neg_integer(), - Key :: info_type() | [atom()] | atom(). -info(A, B, C, Key) -> info(recon_lib:triple_to_pid(A, B, C), Key). - - -%% @doc Allows to be similar to `erlang:process_info/1', but excludes fields -%% such as the mailbox, which have a tendency to grow and be unsafe when called -%% in production systems. Also includes a few more fields than what is usually -%% given (`monitors', `monitored_by', etc.), and separates the fields in a more -%% readable format based on the type of information contained. -%% -%% Moreover, it will fetch and read information on local processes that were -%% registered locally (an atom), globally (`{global, Name}'), or through -%% another registry supported in the `{via, Module, Name}' syntax (must have a -%% `Module:whereis_name/1' function). Pids can also be passed in as a string -%% (`"<0.39.0>"') or a triple (`{0,39,0}') and will be converted to be used. --spec info(pid_term()) -> [{info_type(), [{info_key(), Value}]}, ...] when - Value :: term(). -info(PidTerm) -> - Pid = recon_lib:term_to_pid(PidTerm), - [info(Pid, Type) || Type <- [meta, signals, location, memory_used, work]]. - -%% @doc Allows to be similar to `erlang:process_info/2', but allows to -%% sort fields by safe categories and pre-selections, avoiding items such -%% as the mailbox, which may have a tendency to grow and be unsafe when -%% called in production systems. -%% -%% Moreover, it will fetch and read information on local processes that were -%% registered locally (an atom), globally (`{global, Name}'), or through -%% another registry supported in the `{via, Module, Name}' syntax (must have a -%% `Module:whereis_name/1' function). Pids can also be passed in as a string -%% (`"<0.39.0>"') or a triple (`{0,39,0}') and will be converted to be used. -%% -%% Although the type signature doesn't show it in generated documentation, -%% a list of arguments or individual arguments accepted by -%% `erlang:process_info/2' and return them as that function would. -%% -%% A fake attribute `binary_memory' is also available to return the -%% amount of memory used by refc binaries for a process. --spec info(pid_term(), info_type()) -> {info_type(), [{info_key(), term()}]} -; (pid_term(), [atom()]) -> [{atom(), term()}] -; (pid_term(), atom()) -> {atom(), term()}. -info(PidTerm, meta) -> - info_type(PidTerm, meta, [registered_name, dictionary, group_leader, - status]); -info(PidTerm, signals) -> - info_type(PidTerm, signals, [links, monitors, monitored_by, trap_exit]); -info(PidTerm, location) -> - info_type(PidTerm, location, [initial_call, current_stacktrace]); -info(PidTerm, memory_used) -> - info_type(PidTerm, memory_used, [memory, message_queue_len, heap_size, - total_heap_size, garbage_collection]); -info(PidTerm, work) -> - info_type(PidTerm, work, [reductions]); -info(PidTerm, Keys) -> - proc_info(recon_lib:term_to_pid(PidTerm), Keys). - -%% @private makes access to `info_type()' calls simpler. --spec info_type(pid_term(), info_type(), [info_key()]) -> - {info_type(), [{info_key(), term()}]}. -info_type(PidTerm, Type, Keys) -> - Pid = recon_lib:term_to_pid(PidTerm), - {Type, proc_info(Pid, Keys)}. - -%% @private wrapper around `erlang:process_info/2' that allows special -%% attribute handling for items like `binary_memory'. -proc_info(Pid, binary_memory) -> - {binary, Bins} = erlang:process_info(Pid, binary), - {binary_memory, recon_lib:binary_memory(Bins)}; -proc_info(Pid, Term) when is_atom(Term) -> - erlang:process_info(Pid, Term); -proc_info(Pid, List) when is_list(List) -> - case lists:member(binary_memory, List) of - false -> - erlang:process_info(Pid, List); - true -> - Res = erlang:process_info(Pid, replace(binary_memory, binary, List)), - proc_fake(List, Res) - end. - -%% @private Replace keys around -replace(_, _, []) -> []; -replace(H, Val, [H | T]) -> [Val | replace(H, Val, T)]; -replace(R, Val, [H | T]) -> [H | replace(R, Val, T)]. - -proc_fake([], []) -> - []; -proc_fake([binary_memory | T1], [{binary, Bins} | T2]) -> - [{binary_memory, recon_lib:binary_memory(Bins)} - | proc_fake(T1, T2)]; -proc_fake([_ | T1], [H | T2]) -> - [H | proc_fake(T1, T2)]. - -%% @doc Fetches a given attribute from all processes (except the -%% caller) and returns the biggest `Num' consumers. --spec proc_count(AttributeName, Num) -> [proc_attrs()] when - AttributeName :: atom(), - Num :: non_neg_integer(). -proc_count(AttrName, Num) -> - recon_lib:sublist_top_n_attrs(recon_lib:proc_attrs(AttrName), Num). - -%% @doc Fetches a given attribute from all processes (except the -%% caller) and returns the biggest entries, over a sliding time window. -%% -%% This function is particularly useful when processes on the node -%% are mostly short-lived, usually too short to inspect through other -%% tools, in order to figure out what kind of processes are eating -%% through a lot resources on a given node. -%% -%% It is important to see this function as a snapshot over a sliding -%% window. A program's timeline during sampling might look like this: -%% -%% `--w---- [Sample1] ---x-------------y----- [Sample2] ---z--->' -%% -%% Some processes will live between `w' and die at `x', some between `y' and -%% `z', and some between `x' and `y'. These samples will not be too significant -%% as they're incomplete. If the majority of your processes run between a time -%% interval `x'...`y' (in absolute terms), you should make sure that your -%% sampling time is smaller than this so that for many processes, their -%% lifetime spans the equivalent of `w' and `z'. Not doing this can skew the -%% results: long-lived processes, that have 10 times the time to accumulate -%% data (say reductions) will look like bottlenecks when they're not one. -%% -%% Warning: this function depends on data gathered at two snapshots, and then -%% building a dictionary with entries to differentiate them. This can take a -%% heavy toll on memory when you have many dozens of thousands of processes. --spec proc_window(AttributeName, Num, Milliseconds) -> [proc_attrs()] when - AttributeName :: atom(), - Num :: non_neg_integer(), - Milliseconds :: pos_integer(). -proc_window(AttrName, Num, Time) -> - Sample = fun() -> recon_lib:proc_attrs(AttrName) end, - {First, Last} = recon_lib:sample(Time, Sample), - recon_lib:sublist_top_n_attrs(recon_lib:sliding_window(First, Last), Num). - -%% @doc Refc binaries can be leaking when barely-busy processes route them -%% around and do little else, or when extremely busy processes reach a stable -%% amount of memory allocated and do the vast majority of their work with refc -%% binaries. When this happens, it may take a very long while before references -%% get deallocated and refc binaries get to be garbage collected, leading to -%% Out Of Memory crashes. -%% This function fetches the number of refc binary references in each process -%% of the node, garbage collects them, and compares the resulting number of -%% references in each of them. The function then returns the `N' processes -%% that freed the biggest amount of binaries, potentially highlighting leaks. -%% -%% See The efficiency guide -%% for more details on refc binaries --spec bin_leak(pos_integer()) -> [proc_attrs()]. -bin_leak(N) -> - Procs = recon_lib:sublist_top_n_attrs([ - try - {ok, {_, Pre, Id}} = recon_lib:proc_attrs(binary, Pid), - erlang:garbage_collect(Pid), - {ok, {_, Post, _}} = recon_lib:proc_attrs(binary, Pid), - {Pid, length(Pre) - length(Post), Id} - catch - _:_ -> {Pid, 0, []} - end || Pid <- processes() - ], N), - [{Pid, -Val, Id} || {Pid, Val, Id} <- Procs]. - -%% @doc Shorthand for `node_stats(N, Interval, fun(X,_) -> io:format("~p~n",[X]) end, nostate)'. --spec node_stats_print(Repeat, Interval) -> term() when - Repeat :: non_neg_integer(), - Interval :: pos_integer(). -node_stats_print(N, Interval) -> - node_stats(N, Interval, fun(X, _) -> io:format("~p~n", [X]) end, ok). - -%% @doc Because Erlang CPU usage as reported from `top' isn't the most -%% reliable value (due to schedulers doing idle spinning to avoid going -%% to sleep and impacting latency), a metric exists that is based on -%% scheduler wall time. -%% -%% For any time interval, Scheduler wall time can be used as a measure -%% of how 'busy' a scheduler is. A scheduler is busy when: -%% -%%
    -%%
  • executing process code
  • -%%
  • executing driver code
  • -%%
  • executing NIF code
  • -%%
  • executing BIFs
  • -%%
  • garbage collecting
  • -%%
  • doing memory management
  • -%%
-%% -%% A scheduler isn't busy when doing anything else. --spec scheduler_usage(Millisecs) -> undefined | [{SchedulerId, Usage}] when - Millisecs :: non_neg_integer(), - SchedulerId :: pos_integer(), - Usage :: number(). -scheduler_usage(Interval) when is_integer(Interval) -> - %% We start and stop the scheduler_wall_time system flag if - %% it wasn't in place already. Usually setting the flag should - %% have a CPU impact (making it higher) only when under low usage. - FormerFlag = erlang:system_flag(scheduler_wall_time, true), - First = erlang:statistics(scheduler_wall_time), - timer:sleep(Interval), - Last = erlang:statistics(scheduler_wall_time), - erlang:system_flag(scheduler_wall_time, FormerFlag), - recon_lib:scheduler_usage_diff(First, Last). - -%% @doc Shorthand for `node_stats(N, Interval, fun(X,Acc) -> [X|Acc] end, [])' -%% with the results reversed to be in the right temporal order. --spec node_stats_list(Repeat, Interval) -> [Stats] when - Repeat :: non_neg_integer(), - Interval :: pos_integer(), - Stats :: {[Absolutes :: {atom(), term()}], - [Increments :: {atom(), term()}]}. -node_stats_list(N, Interval) -> - lists:reverse(node_stats(N, Interval, fun(X, Acc) -> [X | Acc] end, [])). - -%% @doc Gathers statistics `N' time, waiting `Interval' milliseconds between -%% each run, and accumulates results using a folding function `FoldFun'. -%% The function will gather statistics in two forms: Absolutes and Increments. -%% -%% Absolutes are values that keep changing with time, and are useful to know -%% about as a datapoint: process count, size of the run queue, error_logger -%% queue length in versions before OTP-21 or those thar run it explicitely, -%% and the memory of the node (total, processes, atoms, binaries, -%% and ets tables). -%% -%% Increments are values that are mostly useful when compared to a previous -%% one to have an idea what they're doing, because otherwise they'd never -%% stop increasing: bytes in and out of the node, number of garbage colelctor -%% runs, words of memory that were garbage collected, and the global reductions -%% count for the node. --spec node_stats(N, Interval, FoldFun, Acc) -> Acc when - N :: non_neg_integer(), - Interval :: pos_integer(), - FoldFun :: fun((Stats, Acc) -> Acc), - Acc :: term(), - Stats :: {[Absolutes :: {atom(), term()}], - [Increments :: {atom(), term()}]}. -node_stats(N, Interval, FoldFun, Init) -> - Logger = case whereis(error_logger) of - undefined -> logger; - _ -> error_logger - end, - %% Turn on scheduler wall time if it wasn't there already - FormerFlag = erlang:system_flag(scheduler_wall_time, true), - %% Stats is an ugly fun, but it does its thing. - Stats = fun({{OldIn, OldOut}, {OldGCs, OldWords, _}, SchedWall}) -> - %% Absolutes - ProcC = erlang:system_info(process_count), - RunQ = erlang:statistics(run_queue), - LogQ = case Logger of - error_logger -> - {_, LogQLen} = process_info(whereis(error_logger), - message_queue_len), - LogQLen; - _ -> - undefined - end, - %% Mem (Absolutes) - Mem = erlang:memory(), - Tot = proplists:get_value(total, Mem), - ProcM = proplists:get_value(processes_used, Mem), - Atom = proplists:get_value(atom_used, Mem), - Bin = proplists:get_value(binary, Mem), - Ets = proplists:get_value(ets, Mem), - %% Incremental - {{input, In}, {output, Out}} = erlang:statistics(io), - GC = {GCs, Words, _} = erlang:statistics(garbage_collection), - BytesIn = In - OldIn, - BytesOut = Out - OldOut, - GCCount = GCs - OldGCs, - GCWords = Words - OldWords, - {_, Reds} = erlang:statistics(reductions), - SchedWallNew = erlang:statistics(scheduler_wall_time), - SchedUsage = recon_lib:scheduler_usage_diff(SchedWall, SchedWallNew), - %% Stats Results - {{[{process_count, ProcC}, {run_queue, RunQ}] ++ - [{error_logger_queue_len, LogQ} || LogQ =/= undefined] ++ - [{memory_total, Tot}, - {memory_procs, ProcM}, {memory_atoms, Atom}, - {memory_bin, Bin}, {memory_ets, Ets}], - [{bytes_in, BytesIn}, {bytes_out, BytesOut}, - {gc_count, GCCount}, {gc_words_reclaimed, GCWords}, - {reductions, Reds}, {scheduler_usage, SchedUsage}]}, - %% New State - {{In, Out}, GC, SchedWallNew}} - end, - {{input, In}, {output, Out}} = erlang:statistics(io), - Gc = erlang:statistics(garbage_collection), - SchedWall = erlang:statistics(scheduler_wall_time), - Result = recon_lib:time_fold( - N, Interval, Stats, - {{In, Out}, Gc, SchedWall}, - FoldFun, Init), - %% Set scheduler wall time back to what it was - erlang:system_flag(scheduler_wall_time, FormerFlag), - Result. - -%%% OTP & Manipulations %%% - - -%% @doc Shorthand call to `recon:get_state(PidTerm, 5000)' --spec get_state(pid_term()) -> term(). -get_state(PidTerm) -> get_state(PidTerm, 5000). - -%% @doc Fetch the internal state of an OTP process. -%% Calls `sys:get_state/2' directly in R16B01+, and fetches -%% it dynamically on older versions of OTP. --spec get_state(pid_term(), Ms :: non_neg_integer() | 'infinity') -> term(). -get_state(PidTerm, Timeout) -> - Proc = recon_lib:term_to_pid(PidTerm), - try - sys:get_state(Proc, Timeout) - catch - error:undef -> - case sys:get_status(Proc, Timeout) of - {status, _Pid, {module, gen_server}, Data} -> - {data, Props} = lists:last(lists:nth(5, Data)), - proplists:get_value("State", Props); - {status, _Pod, {module, gen_fsm}, Data} -> - {data, Props} = lists:last(lists:nth(5, Data)), - proplists:get_value("StateData", Props) - end - end. - -%%% Code & Stuff %%% - -%% @equiv remote_load(nodes(), Mod) --spec remote_load(module()) -> term(). -remote_load(Mod) -> remote_load(nodes(), Mod). - -%% @doc Loads one or more modules remotely, in a diskless manner. Allows to -%% share code loaded locally with a remote node that doesn't have it --spec remote_load(Nodes, module()) -> term() when - Nodes :: [node(), ...] | node(). -remote_load(Nodes = [_ | _], Mod) when is_atom(Mod) -> - {Mod, Bin, File} = code:get_object_code(Mod), - erpc:multicall(Nodes, code, load_binary, [Mod, File, Bin]); -remote_load(Nodes = [_ | _], Modules) when is_list(Modules) -> - [remote_load(Nodes, Mod) || Mod <- Modules]; -remote_load(Node, Mod) -> - remote_load([Node], Mod). - -%% @doc Obtain the source code of a module compiled with `debug_info'. -%% The returned list sadly does not allow to format the types and typed -%% records the way they look in the original module, but instead goes to -%% an intermediary form used in the AST. They will still be placed -%% in the right module attributes, however. -%% @todo Figure out a way to pretty-print typespecs and records. --spec source(module()) -> iolist(). -source(Module) -> - Path = code:which(Module), - {ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(Path, [abstract_code]), - erl_prettypr:format(erl_syntax:form_list(AC)). - -%%% Ports Info %%% - -%% @doc returns a list of all TCP ports (the data type) open on the node. --spec tcp() -> [port()]. -tcp() -> recon_lib:port_list(name, "tcp_inet"). - -%% @doc returns a list of all UDP ports (the data type) open on the node. --spec udp() -> [port()]. -udp() -> recon_lib:port_list(name, "udp_inet"). - -%% @doc returns a list of all SCTP ports (the data type) open on the node. --spec sctp() -> [port()]. -sctp() -> recon_lib:port_list(name, "sctp_inet"). - -%% @doc returns a list of all file handles open on the node. -%% @deprecated Starting with OTP-21, files are implemented as NIFs -%% and can no longer be listed. This function returns an empty list -%% in such a case. --spec files() -> [port()]. -files() -> recon_lib:port_list(name, "efile"). - -%% @doc Shows a list of all different ports on the node with their respective -%% types. --spec port_types() -> [{Type :: string(), Count :: pos_integer()}]. -port_types() -> - lists:usort( - %% sorts by biggest count, smallest type - fun({KA, VA}, {KB, VB}) -> {VA, KB} > {VB, KA} end, - recon_lib:count([Name || {_, Name} <- recon_lib:port_list(name)]) - ). - -%% @doc Fetches a given attribute from all inet ports (TCP, UDP, SCTP) -%% and returns the biggest `Num' consumers. -%% -%% The values to be used can be the number of octets (bytes) sent, received, -%% or both (`send_oct', `recv_oct', `oct', respectively), or the number -%% of packets sent, received, or both (`send_cnt', `recv_cnt', `cnt', -%% respectively). Individual absolute values for each metric will be returned -%% in the 3rd position of the resulting tuple. --spec inet_count(AttributeName, Num) -> [inet_attrs()] when - AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct' - | 'cnt' | 'oct', - Num :: non_neg_integer(). -inet_count(Attr, Num) -> - recon_lib:sublist_top_n_attrs(recon_lib:inet_attrs(Attr), Num). - -%% @doc Fetches a given attribute from all inet ports (TCP, UDP, SCTP) -%% and returns the biggest entries, over a sliding time window. -%% -%% Warning: this function depends on data gathered at two snapshots, and then -%% building a dictionary with entries to differentiate them. This can take a -%% heavy toll on memory when you have many dozens of thousands of ports open. -%% -%% The values to be used can be the number of octets (bytes) sent, received, -%% or both (`send_oct', `recv_oct', `oct', respectively), or the number -%% of packets sent, received, or both (`send_cnt', `recv_cnt', `cnt', -%% respectively). Individual absolute values for each metric will be returned -%% in the 3rd position of the resulting tuple. --spec inet_window(AttributeName, Num, Milliseconds) -> [inet_attrs()] when - AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct' - | 'cnt' | 'oct', - Num :: non_neg_integer(), - Milliseconds :: pos_integer(). -inet_window(Attr, Num, Time) when is_atom(Attr) -> - Sample = fun() -> recon_lib:inet_attrs(Attr) end, - {First, Last} = recon_lib:sample(Time, Sample), - recon_lib:sublist_top_n_attrs(recon_lib:sliding_window(First, Last), Num). - -%% @doc Allows to be similar to `erlang:port_info/1', but allows -%% more flexible port usage: usual ports, ports that were registered -%% locally (an atom), ports represented as strings (`"#Port<0.2013>"'), -%% or through an index lookup (`2013', for the same result as -%% `"#Port<0.2013>"'). -%% -%% Moreover, the function will try to fetch implementation-specific -%% details based on the port type (only inet ports have this feature -%% so far). For example, TCP ports will include information about the -%% remote peer, transfer statistics, and socket options being used. -%% -%% The information-specific and the basic port info are sorted and -%% categorized in broader categories ({@link port_info_type()}). --spec port_info(port_term()) -> [{port_info_type(), - [{port_info_key(), term()}]}, ...]. -port_info(PortTerm) -> - Port = recon_lib:term_to_port(PortTerm), - [port_info(Port, Type) || Type <- [meta, signals, io, memory_used, - specific]]. - -%% @doc Allows to be similar to `erlang:port_info/2', but allows -%% more flexible port usage: usual ports, ports that were registered -%% locally (an atom), ports represented as strings (`"#Port<0.2013>"'), -%% or through an index lookup (`2013', for the same result as -%% `"#Port<0.2013>"'). -%% -%% Moreover, the function allows to to fetch information by category -%% as defined in {@link port_info_type()}, and although the type signature -%% doesn't show it in the generated documentation, individual items -%% accepted by `erlang:port_info/2' are accepted, and lists of them too. --spec port_info(port_term(), port_info_type()) -> {port_info_type(), - [{port_info_key(), _}]} -; (port_term(), [atom()]) -> [{atom(), term()}] -; (port_term(), atom()) -> {atom(), term()}. -port_info(PortTerm, meta) -> - {meta, List} = port_info_type(PortTerm, meta, [id, name, os_pid]), - case port_info(PortTerm, registered_name) of - [] -> {meta, List}; - Name -> {meta, [Name | List]} - end; -port_info(PortTerm, signals) -> - port_info_type(PortTerm, signals, [connected, links, monitors]); -port_info(PortTerm, io) -> - port_info_type(PortTerm, io, [input, output]); -port_info(PortTerm, memory_used) -> - port_info_type(PortTerm, memory_used, [memory, queue_size]); -port_info(PortTerm, specific) -> - Port = recon_lib:term_to_port(PortTerm), - Props = case erlang:port_info(Port, name) of - {_, Type} when Type =:= "udp_inet"; - Type =:= "tcp_inet"; - Type =:= "sctp_inet" -> - case inet:getstat(Port) of - {ok, Stats} -> [{statistics, Stats}]; - _ -> [] - end ++ - case inet:peername(Port) of - {ok, Peer} -> [{peername, Peer}]; - {error, _} -> [] - end ++ - case inet:sockname(Port) of - {ok, Local} -> [{sockname, Local}]; - {error, _} -> [] - end ++ - case inet:getopts(Port, [active, broadcast, buffer, delay_send, - dontroute, exit_on_close, header, - high_watermark, ipv6_v6only, keepalive, - linger, low_watermark, mode, nodelay, - packet, packet_size, priority, - read_packets, recbuf, reuseaddr, - send_timeout, sndbuf]) of - {ok, Opts} -> [{options, Opts}]; - {error, _} -> [] - end; - {_, "efile"} -> - %% would be nice to support file-specific info, but things - %% are too vague with the file_server and how it works in - %% order to make this work efficiently - []; - _ -> - [] - end, - {type, Props}; -port_info(PortTerm, Keys) when is_list(Keys) -> - Port = recon_lib:term_to_port(PortTerm), - [erlang:port_info(Port, Key) || Key <- Keys]; -port_info(PortTerm, Key) when is_atom(Key) -> - erlang:port_info(recon_lib:term_to_port(PortTerm), Key). - -%% @private makes access to `port_info_type()' calls simpler. -%-spec port_info_type(pid_term(), port_info_type(), [port_info_key()]) -> -% {port_info_type(), [{port_info_key(), term()}]}. -port_info_type(PortTerm, Type, Keys) -> - Port = recon_lib:term_to_port(PortTerm), - {Type, [erlang:port_info(Port, Key) || Key <- Keys]}. - - -%%% RPC Utils %%% - -%% @doc Shorthand for `rpc([node()|nodes()], Fun)'. --spec rpc(fun(() -> term())) -> {[Success :: _], [Fail :: _]}. -rpc(Fun) -> - rpc([node() | nodes()], Fun). - -%% @doc Shorthand for `rpc(Nodes, Fun, infinity)'. --spec rpc(node()|[node(), ...], fun(() -> term())) -> {[Success :: _], [Fail :: _]}. -rpc(Nodes, Fun) -> - rpc(Nodes, Fun, infinity). - -%% @doc Runs an arbitrary fun (of arity 0) over one or more nodes. --spec rpc(node()|[node(), ...], fun(() -> term()), timeout()) -> {[Success :: _], [Fail :: _]}. -rpc(Nodes = [_ | _], Fun, Timeout) when is_function(Fun, 0) -> - erpc:multicall(Nodes, erlang, apply, [Fun, []], Timeout); -rpc(Node, Fun, Timeout) when is_atom(Node) -> - rpc([Node], Fun, Timeout). - -%% @doc Shorthand for `named_rpc([node()|nodes()], Fun)'. --spec named_rpc(fun(() -> term())) -> {[Success :: _], [Fail :: _]}. -named_rpc(Fun) -> - named_rpc([node() | nodes()], Fun). - -%% @doc Shorthand for `named_rpc(Nodes, Fun, infinity)'. --spec named_rpc(node()|[node(), ...], fun(() -> term())) -> {[Success :: _], [Fail :: _]}. -named_rpc(Nodes, Fun) -> - named_rpc(Nodes, Fun, infinity). - -%% @doc Runs an arbitrary fun (of arity 0) over one or more nodes, and returns the -%% name of the node that computed a given result along with it, in a tuple. --spec named_rpc(node()|[node(), ...], fun(() -> term()), timeout()) -> {[Success :: _], [Fail :: _]}. -named_rpc(Nodes = [_ | _], Fun, Timeout) when is_function(Fun, 0) -> - erpc:multicall(Nodes, erlang, apply, [fun() -> {node(), Fun()} end, []], Timeout); -named_rpc(Node, Fun, Timeout) when is_atom(Node) -> - named_rpc([Node], Fun, Timeout). - diff --git a/src/test/recon-2.5.1/recon_alloc.erl b/src/test/recon-2.5.1/recon_alloc.erl deleted file mode 100644 index fb62a5e..0000000 --- a/src/test/recon-2.5.1/recon_alloc.erl +++ /dev/null @@ -1,778 +0,0 @@ -%%% @author Fred Hebert -%%% [http://ferd.ca/] -%%% @author Lukas Larsson -%%% @doc Functions to deal with -%%% Erlang's memory -%%% allocators, or particularly, to try to present the allocator data -%%% in a way that makes it simpler to discover possible problems. -%%% -%%% Tweaking Erlang memory allocators and their behaviour is a very tricky -%%% ordeal whenever you have to give up the default settings. This module -%%% (and its documentation) will try and provide helpful pointers to help -%%% in this task. -%%% -%%% This module should mostly be helpful to figure out if there is -%%% a problem, but will offer little help to figure out what is wrong. -%%% -%%% To figure this out, you need to dig deeper into the allocator data -%%% (obtainable with {@link allocators/0}), and/or have some precise knowledge -%%% about the type of load and work done by the VM to be able to assess what -%%% each reaction to individual tweak should be. -%%% -%%% A lot of trial and error might be required to figure out if tweaks have -%%% helped or not, ultimately. -%%% -%%% In order to help do offline debugging of memory allocator problems -%%% recon_alloc also has a few functions that store snapshots of the -%%% memory statistics. -%%% These snapshots can be used to freeze the current allocation values so that -%%% they do not change during analysis while using the regular functionality of -%%% this module, so that the allocator values can be saved, or that -%%% they can be shared, dumped, and reloaded for further analysis using files. -%%% See {@link snapshot_load/1} for a simple use-case. -%%% -%%% Glossary: -%%%
-%%%
sys_alloc
-%%%
System allocator, usually just malloc
-%%% -%%%
mseg_alloc
-%%%
Used by other allocators, can do mmap. Caches allocations
-%%% -%%%
temp_alloc
-%%%
Used for temporary allocations
-%%% -%%%
eheap_alloc
-%%%
Heap data (i.e. process heaps) allocator
-%%% -%%%
binary_alloc
-%%%
Global binary heap allocator
-%%% -%%%
ets_alloc
-%%%
ETS data allocator
-%%% -%%%
driver_alloc
-%%%
Driver data allocator
-%%% -%%%
sl_alloc
-%%%
Short-lived memory blocks allocator
-%%% -%%%
ll_alloc
-%%%
Long-lived data (i.e. Erlang code itself) allocator
-%%% -%%%
fix_alloc
-%%%
Frequently used fixed-size data allocator
-%%% -%%%
std_alloc
-%%%
Allocator for other memory blocks
-%%% -%%%
carrier
-%%%
When a given area of memory is allocated by the OS to the -%%% VM (through sys_alloc or mseg_alloc), it is put into a 'carrier'. There -%%% are two kinds of carriers: multiblock and single block. The default -%%% carriers data is sent to are multiblock carriers, owned by a specific -%%% allocator (ets_alloc, binary_alloc, etc.). The specific allocator can -%%% thus do allocation for specific Erlang requirements within bits of -%%% memory that has been preallocated before. This allows more reuse, -%%% and we can even measure the cache hit rates {@link cache_hit_rates/0}. -%%% -%%% There is however a threshold above which an item in memory won't fit -%%% a multiblock carrier. When that happens, the specific allocator does -%%% a special allocation to a single block carrier. This is done by the -%%% allocator basically asking for space directly from sys_alloc or -%%% mseg_alloc rather than a previously multiblock area already obtained -%%% before. -%%% -%%% This leads to various allocation strategies where you decide to -%%% choose: -%%%
    -%%%
  1. which multiblock carrier you're going to (if at all)
  2. -%%%
  3. which block in that carrier you're going to
  4. -%%%
-%%% -%%% See the official -%%% documentation on erts_alloc for more details. -%%%
-%%% -%%%
mbcs
-%%%
Multiblock carriers.
-%%% -%%%
sbcs
-%%%
Single block carriers.
-%%% -%%%
lmbcs
-%%%
Largest multiblock carrier size
-%%% -%%%
smbcs
-%%%
Smallest multiblock carrier size
-%%% -%%%
sbct
-%%%
Single block carrier threshold
-%%%
-%%% -%%% By default all sizes returned by this module are in bytes. You can change -%%% this by calling {@link set_unit/1}. -%%% --module(recon_alloc). --define(UTIL_ALLOCATORS, [temp_alloc, - eheap_alloc, - binary_alloc, - ets_alloc, - driver_alloc, - sl_alloc, - ll_alloc, - fix_alloc, - std_alloc -]). - --type allocator() :: temp_alloc | eheap_alloc | binary_alloc | ets_alloc -| driver_alloc | sl_alloc | ll_alloc | fix_alloc -| std_alloc. --type instance() :: non_neg_integer(). --type allocdata(T) :: {{allocator(), instance()}, T}. --type allocdata_types(T) :: {{allocator(), [instance()]}, T}. --export_type([allocator/0, instance/0, allocdata/1]). - --define(CURRENT_POS, 2). % pos in sizes tuples for current value --define(MAX_POS, 4). % pos in sizes tuples for max value - --export([memory/1, memory/2, fragmentation/1, cache_hit_rates/0, - average_block_sizes/1, sbcs_to_mbcs/1, allocators/0, - allocators/1]). - -%% Snapshot handling --type memory() :: [{atom(), atom()}]. --type snapshot() :: {memory(), [allocdata(term())]}. - --export_type([memory/0, snapshot/0]). - --export([snapshot/0, snapshot_clear/0, - snapshot_print/0, snapshot_get/0, - snapshot_save/1, snapshot_load/1]). - -%% Unit handling --export([set_unit/1]). - -%%%%%%%%%%%%%% -%%% Public %%% -%%%%%%%%%%%%%% - - -%% @doc Equivalent to `memory(Key, current)'. --spec memory(used | allocated | unused) -> pos_integer() -; (usage) -> number() -; (allocated_types | allocated_instances) -> - [{allocator(), pos_integer()}]. -memory(Key) -> memory(Key, current). - -%% @doc reports one of multiple possible memory values for the entire -%% node depending on what is to be reported: -%% -%%
    -%%
  • `used' reports the memory that is actively used for allocated -%% Erlang data;
  • -%%
  • `allocated' reports the memory that is reserved by the VM. It -%% includes the memory used, but also the memory yet-to-be-used but still -%% given by the OS. This is the amount you want if you're dealing with -%% ulimit and OS-reported values.
  • -%%
  • `allocated_types' report the memory that is reserved by the -%% VM grouped into the different util allocators.
  • -%%
  • `allocated_instances' report the memory that is reserved -%% by the VM grouped into the different schedulers. Note that -%% instance id 0 is the global allocator used to allocate data from -%% non-managed threads, i.e. async and driver threads.
  • -%%
  • `unused' reports the amount of memory reserved by the VM that -%% is not being allocated. -%% Equivalent to `allocated - used'.
  • -%%
  • `usage' returns a percentage (0.0 .. 1.0) of `used/allocated' -%% memory ratios.
  • -%%
-%% -%% The memory reported by `allocated' should roughly -%% match what the OS reports. If this amount is different by a large margin, -%% it may be the sign that someone is allocating memory in C directly, outside -%% of Erlang's own allocator -- a big warning sign. There are currently -%% three sources of memory alloction that are not counted towards this value: -%% The cached segments in the mseg allocator, any memory allocated as a -%% super carrier, and small pieces of memory allocated during startup -%% before the memory allocators are initialized. -%% -%% Also note that low memory usages can be the sign of fragmentation in -%% memory, in which case exploring which specific allocator is at fault -%% is recommended (see {@link fragmentation/1}) --spec memory(used | allocated | unused, current | max) -> pos_integer() -; (usage, current | max) -> number() -; (allocated_types|allocated_instances, current | max) -> - [{allocator(), pos_integer()}]. -memory(used, Keyword) -> - lists:sum(lists:map(fun({_, Prop}) -> - container_size(Prop, Keyword, blocks_size) - end, util_alloc())); -memory(allocated, Keyword) -> - lists:sum(lists:map(fun({_, Prop}) -> - container_size(Prop, Keyword, carriers_size) - end, util_alloc())); -memory(allocated_types, Keyword) -> - lists:foldl(fun({{Alloc, _N}, Props}, Acc) -> - CZ = container_size(Props, Keyword, carriers_size), - orddict:update_counter(Alloc, CZ, Acc) - end, orddict:new(), util_alloc()); -memory(allocated_instances, Keyword) -> - lists:foldl(fun({{_Alloc, N}, Props}, Acc) -> - CZ = container_size(Props, Keyword, carriers_size), - orddict:update_counter(N, CZ, Acc) - end, orddict:new(), util_alloc()); -memory(unused, Keyword) -> - memory(allocated, Keyword) - memory(used, Keyword); -memory(usage, Keyword) -> - memory(used, Keyword) / memory(allocated, Keyword). - -%% @doc Compares the block sizes to the carrier sizes, both for -%% single block (`sbcs') and multiblock (`mbcs') carriers. -%% -%% The returned results are sorted by a weight system that is -%% somewhat likely to return the most fragmented allocators first, -%% based on their percentage of use and the total size of the carriers, -%% for both `sbcs' and `mbcs'. -%% -%% The values can both be returned for `current' allocator values, and -%% for `max' allocator values. The current values hold the present allocation -%% numbers, and max values, the values at the peak. Comparing both together -%% can give an idea of whether the node is currently being at its memory peak -%% when possibly leaky, or if it isn't. This information can in turn -%% influence the tuning of allocators to better fit sizes of blocks and/or -%% carriers. --spec fragmentation(current | max) -> [allocdata([{atom(), term()}])]. -fragmentation(Keyword) -> - WeighedData = [begin - BlockSbcs = container_value(Props, Keyword, sbcs, blocks_size), - CarSbcs = container_value(Props, Keyword, sbcs, carriers_size), - BlockMbcs = container_value(Props, Keyword, mbcs, blocks_size), - CarMbcs = container_value(Props, Keyword, mbcs, carriers_size), - {Weight, Vals} = weighed_values({BlockSbcs, CarSbcs}, - {BlockMbcs, CarMbcs}), - {Weight, {Allocator, N}, Vals} - end || {{Allocator, N}, Props} <- util_alloc()], - [{Key, Val} || {_W, Key, Val} <- lists:reverse(lists:sort(WeighedData))]. - -%% @doc looks at the `mseg_alloc' allocator (allocator used by all the -%% allocators in {@link allocator()}) and returns information relative to -%% the cache hit rates. Unless memory has expected spiky behaviour, it should -%% usually be above 0.80 (80%). -%% -%% Cache can be tweaked using three VM flags: `+MMmcs', `+MMrmcbf', and -%% `+MMamcbf'. -%% -%% `+MMmcs' stands for the maximum amount of cached memory segments. Its -%% default value is '10' and can be anything from 0 to 30. Increasing -%% it first and verifying if cache hits get better should be the first -%% step taken. -%% -%% The two other options specify what are the maximal values of a segment -%% to cache, in relative (in percent) and absolute terms (in kilobytes), -%% respectively. Increasing these may allow more segments to be cached, but -%% should also add overheads to memory allocation. An Erlang node that has -%% limited memory and increases these values may make things worse on -%% that point. -%% -%% The values returned by this function are sorted by a weight combining -%% the lower cache hit joined to the largest memory values allocated. --spec cache_hit_rates() -> [{{instance, instance()}, [{Key, Val}]}] when - Key :: hit_rate | hits | calls, - Val :: term(). -cache_hit_rates() -> - WeighedData = [begin - Mem = proplists:get_value(memkind, Props), - {_, Hits} = lists:keyfind(cache_hits, 1, proplists:get_value(status, Mem)), - {_, Giga, Ones} = lists:keyfind(mseg_alloc, 1, proplists:get_value(calls, Mem)), - Calls = 1000000000 * Giga + Ones, - HitRate = usage(Hits, Calls), - Weight = (1.00 - HitRate) * Calls, - {Weight, {instance, N}, [{hit_rate, HitRate}, {hits, Hits}, {calls, Calls}]} - end || {{_, N}, Props} <- alloc([mseg_alloc])], - [{Key, Val} || {_W, Key, Val} <- lists:reverse(lists:sort(WeighedData))]. - -%% @doc Checks all allocators in {@link allocator()} and returns the average -%% block sizes being used for `mbcs' and `sbcs'. This value is interesting -%% to use because it will tell us how large most blocks are. -%% This can be related to the VM's largest multiblock carrier size -%% (`lmbcs') and smallest multiblock carrier size (`smbcs') to specify -%% allocation strategies regarding the carrier sizes to be used. -%% -%% This function isn't exceptionally useful unless you know you have some -%% specific problem, say with sbcs/mbcs ratios (see {@link sbcs_to_mbcs/0}) -%% or fragmentation for a specific allocator, and want to figure out what -%% values to pick to increase or decrease sizes compared to the currently -%% configured value. -%% -%% Do note that values for `lmbcs' and `smbcs' are going to be rounded up -%% to the next power of two when configuring them. --spec average_block_sizes(current | max) -> [{allocator(), [{Key, Val}]}] when - Key :: mbcs | sbcs, - Val :: number(). -average_block_sizes(Keyword) -> - Dict = lists:foldl(fun({{Instance, _}, Props}, Dict0) -> - CarSbcs = container_value(Props, Keyword, sbcs, blocks), - SizeSbcs = container_value(Props, Keyword, sbcs, blocks_size), - CarMbcs = container_value(Props, Keyword, mbcs, blocks), - SizeMbcs = container_value(Props, Keyword, mbcs, blocks_size), - Dict1 = dict:update_counter({Instance, sbcs, count}, CarSbcs, Dict0), - Dict2 = dict:update_counter({Instance, sbcs, size}, SizeSbcs, Dict1), - Dict3 = dict:update_counter({Instance, mbcs, count}, CarMbcs, Dict2), - Dict4 = dict:update_counter({Instance, mbcs, size}, SizeMbcs, Dict3), - Dict4 - end, - dict:new(), - util_alloc()), - average_group(average_calc(lists:sort(dict:to_list(Dict)))). - -%% @doc compares the amount of single block carriers (`sbcs') vs the -%% number of multiblock carriers (`mbcs') for each individual allocator in -%% {@link allocator()}. -%% -%% When a specific piece of data is allocated, it is compared to a threshold, -%% called the 'single block carrier threshold' (`sbct'). When the data is -%% larger than the `sbct', it gets sent to a single block carrier. When the -%% data is smaller than the `sbct', it gets placed into a multiblock carrier. -%% -%% mbcs are to be preferred to sbcs because they basically represent pre- -%% allocated memory, whereas sbcs will map to one call to sys_alloc -%% or mseg_alloc, which is more expensive than redistributing -%% data that was obtained for multiblock carriers. Moreover, the VM is able to -%% do specific work with mbcs that should help reduce fragmentation in ways -%% sys_alloc or mmap usually won't. -%% -%% Ideally, most of the data should fit inside multiblock carriers. If -%% most of the data ends up in `sbcs', you may need to adjust the multiblock -%% carrier sizes, specifically the maximal value (`lmbcs') and the threshold -%% (`sbct'). On 32 bit VMs, `sbct' is limited to 8MBs, but 64 bit VMs can go -%% to pretty much any practical size. -%% -%% Given the value returned is a ratio of sbcs/mbcs, the higher the value, -%% the worst the condition. The list is sorted accordingly. --spec sbcs_to_mbcs(max | current) -> [allocdata(term())]. -sbcs_to_mbcs(Keyword) -> - WeightedList = [begin - Sbcs = container_value(Props, Keyword, sbcs, blocks), - Mbcs = container_value(Props, Keyword, mbcs, blocks), - Ratio = case {Sbcs, Mbcs} of - {0, 0} -> 0; - {_, 0} -> infinity; % that is bad! - {_, _} -> Sbcs / Mbcs - end, - {Ratio, {Allocator, N}} - end || {{Allocator, N}, Props} <- util_alloc()], - [{Alloc, Ratio} || {Ratio, Alloc} <- lists:reverse(lists:sort(WeightedList))]. - -%% @doc returns a dump of all allocator settings and values --spec allocators() -> [allocdata(term())]. -allocators() -> - UtilAllocators = erlang:system_info(alloc_util_allocators), - Allocators = [sys_alloc, mseg_alloc | UtilAllocators], - [{{A, N}, format_alloc(A, Props)} || - A <- Allocators, - Allocs <- [erlang:system_info({allocator, A})], - Allocs =/= false, - {_, N, Props} <- Allocs]. - -format_alloc(Alloc, Props) -> - %% {versions,_,_} is implicitly deleted in order to allow the use of the - %% orddict api, and never really having come across a case where it was - %% useful to know. - [{K, format_blocks(Alloc, K, V)} || {K, V} <- lists:sort(Props)]. - -format_blocks(_, _, []) -> - []; -format_blocks(Alloc, Key, [{blocks, L} | List]) when is_list(L) -> - %% OTP-22 introduces carrier migrations across types, and OTP-23 changes the - %% format of data reported to be a bit richer; however it's not compatible - %% with most calculations made for this library. - %% So what we do here for `blocks' is merge all the info into the one the - %% library expects (`blocks' and `blocks_size'), then keep the original - %% one in case it is further needed. - %% There were further changes to `mbcs_pool' changing `foreign_blocks', - %% `blocks' and `blocks_size' into just `blocks' with a proplist, so we're breaking - %% up to use that one too. - %% In the end we go from `{blocks, [{Alloc, [...]}]}' to: - %% - `{blocks, ...}' (4-tuple in mbcs and sbcs, 2-tuple in mbcs_pool) - %% - `{blocks_size, ...}' (4-tuple in mbcs and sbcs, 2-tuple in mbcs_pool) - %% - `{foreign_blocks, [...]}' (just append lists =/= `Alloc') - %% - `{raw_blocks, [...]}' (original value) - Foreign = lists:filter(fun({A, _Props}) -> A =/= Alloc end, L), - Type = case Key of - mbcs_pool -> int; - _ -> quadruple - end, - MergeF = fun(K) -> - fun({_A, Props}, Acc) -> - case lists:keyfind(K, 1, Props) of - {K, Cur, Last, Max} -> {Cur, Last, Max}; - {K, V} -> Acc + V - end - end - end, - %% Since tuple sizes change, hack around it using tuple_to_list conversion - %% and set the accumulator to a list so it defaults to not putting anything - {Blocks, BlocksSize} = case Type of - int -> - {{blocks, lists:foldl(MergeF(count), 0, L)}, - {blocks_size, lists:foldl(MergeF(size), 0, L)}}; - quadruple -> - {list_to_tuple([blocks | tuple_to_list(lists:foldl(MergeF(count), {0, 0, 0}, L))]), - list_to_tuple([blocks_size | tuple_to_list(lists:foldl(MergeF(size), {0, 0, 0}, L))])} - end, - [Blocks, BlocksSize, {foreign_blocks, Foreign}, {raw_blocks, L} - | format_blocks(Alloc, Key, List)]; -format_blocks(Alloc, Key, [H | T]) -> - [H | format_blocks(Alloc, Key, T)]. - -%% @doc returns a dump of all allocator settings and values modified -%% depending on the argument. -%%
    -%%
  • `types' report the settings and accumulated values for each -%% allocator type. This is useful when looking for anomalies -%% in the system as a whole and not specific instances.
  • -%%
--spec allocators(types) -> [allocdata_types(term())]. -allocators(types) -> - allocators_types(alloc(), []). - -allocators_types([{{Type, No}, Vs} | T], As) -> - case lists:keytake(Type, 1, As) of - false -> - allocators_types(T, [{Type, [No], sort_values(Type, Vs)} | As]); - {value, {Type, Nos, OVs}, NAs} -> - MergedValues = merge_values(sort_values(Type, Vs), OVs), - allocators_types(T, [{Type, [No | Nos], MergedValues} | NAs]) - end; -allocators_types([], As) -> - [{{Type, Nos}, Vs} || {Type, Nos, Vs} <- As]. - -merge_values([{Key, Vs} | T1], [{Key, OVs} | T2]) when Key =:= memkind -> - [{Key, merge_values(Vs, OVs)} | merge_values(T1, T2)]; -merge_values([{Key, Vs} | T1], [{Key, OVs} | T2]) when Key =:= calls; - Key =:= fix_types; - Key =:= sbmbcs; - Key =:= mbcs; - Key =:= mbcs_pool; - Key =:= sbcs; - Key =:= status -> - [{Key, lists:map( - fun({{K, MV1, V1}, {K, MV2, V2}}) -> - %% Merge the MegaVs + Vs into one - V = MV1 * 1000000 + V1 + MV2 * 1000000 + V2, - {K, V div 1000000, V rem 1000000}; - ({{K, V1}, {K, V2}}) when K =:= segments_watermark -> - %% We take the maximum watermark as that is - %% a value that we can use somewhat. Ideally - %% maybe the average should be used, but the - %% value is very rarely important so leave it - %% like this for now. - {K, lists:max([V1, V2])}; - ({{K, V1}, {K, V2}}) when K =:= foreign_blocks; K =:= raw_blocks -> - %% foreign blocks are just merged as a bigger list. - {K, V1 ++ V2}; - ({{K, V1}, {K, V2}}) -> - {K, V1 + V2}; - ({{K, C1, L1, M1}, {K, C2, L2, M2}}) -> - %% Merge the Curr, Last, Max into one - {K, C1 + C2, L1 + L2, M1 + M2} - end, lists:zip(Vs, OVs))} | merge_values(T1, T2)]; -merge_values([{Type, _Vs} = E | T1], T2) when Type =:= mbcs_pool -> - %% For values never showing up in instance 0 but in all other - [E | merge_values(T1, T2)]; -merge_values(T1, [{Type, _Vs} = E | T2]) when Type =:= fix_types -> - %% For values only showing up in instance 0 - [E | merge_values(T1, T2)]; -merge_values([E | T1], [E | T2]) -> - %% For values that are constant - [E | merge_values(T1, T2)]; -merge_values([{options, _Vs1} | T1], [{options, _Vs2} = E | T2]) -> - %% Options change a but in between instance 0 and the other, - %% We show the others as they are the most interesting. - [E | merge_values(T1, T2)]; -merge_values([], []) -> - []. - -sort_values(mseg_alloc, Vs) -> - {value, {memkind, MemKindVs}, OVs} = lists:keytake(memkind, 1, Vs), - lists:sort([{memkind, lists:sort(MemKindVs)} | OVs]); -sort_values(_Type, Vs) -> - lists:sort(Vs). - -%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Snapshot handling %%% -%%%%%%%%%%%%%%%%%%%%%%%%% - -%% @doc Take a new snapshot of the current memory allocator statistics. -%% The snapshot is stored in the process dictionary of the calling process, -%% with all the limitations that it implies (i.e. no garbage-collection). -%% To unsert the snapshot, see {@link snapshot_clear/1}. --spec snapshot() -> snapshot() | undefined. -snapshot() -> - put(recon_alloc_snapshot, snapshot_int()). - -%% @doc clear the current snapshot in the process dictionary, if present, -%% and return the value it had before being unset. -%% @end -%% Maybe we should use erlang:delete(Key) here instead? --spec snapshot_clear() -> snapshot() | undefined. -snapshot_clear() -> - put(recon_alloc_snapshot, undefined). - -%% @doc print a dump of the current snapshot stored by {@link snapshot/0} -%% Prints `undefined' if no snapshot has been taken. --spec snapshot_print() -> ok. -snapshot_print() -> - io:format("~p.~n", [snapshot_get()]). - -%% @doc returns the current snapshot stored by {@link snapshot/0}. -%% Returns `undefined' if no snapshot has been taken. --spec snapshot_get() -> snapshot() | undefined. -snapshot_get() -> - get(recon_alloc_snapshot). - -%% @doc save the current snapshot taken by {@link snapshot/0} to a file. -%% If there is no current snapshot, a snaphot of the current allocator -%% statistics will be written to the file. --spec snapshot_save(Filename) -> ok when - Filename :: file:name(). -snapshot_save(Filename) -> - Snapshot = case snapshot_get() of - undefined -> - snapshot_int(); - Snap -> - Snap - end, - case file:write_file(Filename, io_lib:format("~p.~n", [Snapshot])) of - ok -> ok; - {error, Reason} -> - erlang:error(Reason, [Filename]) - end. - - -%% @doc load a snapshot from a given file. The format of the data in the -%% file can be either the same as output by {@link snapshot_save()}, -%% or the output obtained by calling -%% `{erlang:memory(),[{A,erlang:system_info({allocator,A})} || A <- erlang:system_info(alloc_util_allocators)++[sys_alloc,mseg_alloc]]}.' -%% and storing it in a file. -%% If the latter option is taken, please remember to add a full stop at the end -%% of the resulting Erlang term, as this function uses `file:consult/1' to load -%% the file. -%% -%% Example usage: -%% -%%```On target machine: -%% 1> recon_alloc:snapshot(). -%% undefined -%% 2> recon_alloc:memory(used). -%% 18411064 -%% 3> recon_alloc:snapshot_save("recon_snapshot.terms"). -%% ok -%% -%% On other machine: -%% 1> recon_alloc:snapshot_load("recon_snapshot.terms"). -%% undefined -%% 2> recon_alloc:memory(used). -%% 18411064''' -%% --spec snapshot_load(Filename) -> snapshot() | undefined when - Filename :: file:name(). -snapshot_load(Filename) -> - {ok, [Terms]} = file:consult(Filename), - Snapshot = - case Terms of - %% We handle someone using - %% {erlang:memory(), - %% [{A,erlang:system_info({allocator,A})} || - %% A <- erlang:system_info(alloc_util_allocators)++[sys_alloc,mseg_alloc]]} - %% to dump data. - {M, [{Alloc, _D} | _] = Allocs} when is_atom(Alloc) -> - {M, [{{A, N}, lists:sort(proplists:delete(versions, Props))} || - {A, Instances = [_ | _]} <- Allocs, - {_, N, Props} <- Instances]}; - %% We assume someone used recon_alloc:snapshot() to store this one - {M, Allocs} -> - {M, [{AN, lists:sort(proplists:delete(versions, Props))} || - {AN, Props} <- Allocs]} - end, - put(recon_alloc_snapshot, Snapshot). - -%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Handling of units %%% -%%%%%%%%%%%%%%%%%%%%%%%%% - -%% @doc set the current unit to be used by recon_alloc. This effects all -%% functions that return bytes. -%% -%% Eg. -%% ```1> recon_alloc:memory(used,current). -%% 17548752 -%% 2> recon_alloc:set_unit(kilobyte). -%% undefined -%% 3> recon_alloc:memory(used,current). -%% 17576.90625''' -%% --spec set_unit(byte | kilobyte | megabyte | gigabyte) -> ok. -set_unit(byte) -> - put(recon_alloc_unit, undefined); -set_unit(kilobyte) -> - put(recon_alloc_unit, 1024); -set_unit(megabyte) -> - put(recon_alloc_unit, 1024 * 1024); -set_unit(gigabyte) -> - put(recon_alloc_unit, 1024 * 1024 * 1024). - -conv({Mem, Allocs} = D) -> - case get(recon_alloc_unit) of - undefined -> - D; - Factor -> - {conv_mem(Mem, Factor), conv_alloc(Allocs, Factor)} - end. - -conv_mem(Mem, Factor) -> - [{T, M / Factor} || {T, M} <- Mem]. - -conv_alloc([{{sys_alloc, _I}, _Props} = Alloc | R], Factor) -> - [Alloc | conv_alloc(R, Factor)]; -conv_alloc([{{mseg_alloc, _I} = AI, Props} | R], Factor) -> - MemKind = orddict:fetch(memkind, Props), - Status = orddict:fetch(status, MemKind), - {segments_size, Curr, Last, Max} = lists:keyfind(segments_size, 1, Status), - NewSegSize = {segments_size, Curr / Factor, Last / Factor, Max / Factor}, - NewStatus = lists:keyreplace(segments_size, 1, Status, NewSegSize), - NewProps = orddict:store(memkind, orddict:store(status, NewStatus, MemKind), - Props), - [{AI, NewProps} | conv_alloc(R, Factor)]; -conv_alloc([{AI, Props} | R], Factor) -> - FactorFun = fun({T, Curr}) when - T =:= blocks_size; T =:= carriers_size -> - {T, Curr / Factor}; - ({T, Curr, Last, Max}) when - T =:= blocks_size; T =:= carriers_size; - T =:= mseg_alloc_carriers_size; - T =:= sys_alloc_carriers_size -> - {T, Curr / Factor, Last / Factor, Max / Factor}; - (T) -> - T - end, - NewMbcsProp = [FactorFun(Prop) || Prop <- orddict:fetch(mbcs, Props)], - NewSbcsProp = [FactorFun(Prop) || Prop <- orddict:fetch(sbcs, Props)], - NewProps = orddict:store(sbcs, NewSbcsProp, - orddict:store(mbcs, NewMbcsProp, Props)), - case orddict:find(mbcs_pool, Props) of - error -> - [{AI, NewProps} | conv_alloc(R, Factor)]; - {ok, MbcsPoolProps} -> - NewMbcsPoolProp = [FactorFun(Prop) || Prop <- MbcsPoolProps], - NewPoolProps = orddict:store(mbcs_pool, NewMbcsPoolProp, NewProps), - [{AI, NewPoolProps} | conv_alloc(R, Factor)] - end; -conv_alloc([], _Factor) -> - []. - -%%%%%%%%%%%%%%% -%%% Private %%% -%%%%%%%%%%%%%%% - -%% Sort on small usage vs large size. -%% The weight cares about both the sbcs and mbcs values, and also -%% returns a proplist of possibly interesting values. -weighed_values({SbcsBlockSize, SbcsCarrierSize}, - {MbcsBlockSize, MbcsCarrierSize}) -> - SbcsUsage = usage(SbcsBlockSize, SbcsCarrierSize), - MbcsUsage = usage(MbcsBlockSize, MbcsCarrierSize), - SbcsWeight = (1.00 - SbcsUsage) * SbcsCarrierSize, - MbcsWeight = (1.00 - MbcsUsage) * MbcsCarrierSize, - Weight = SbcsWeight + MbcsWeight, - {Weight, [{sbcs_usage, SbcsUsage}, - {mbcs_usage, MbcsUsage}, - {sbcs_block_size, SbcsBlockSize}, - {sbcs_carriers_size, SbcsCarrierSize}, - {mbcs_block_size, MbcsBlockSize}, - {mbcs_carriers_size, MbcsCarrierSize}]}. - -%% Returns the `BlockSize/CarrierSize' as a 0.0 -> 1.0 percentage, -%% but also takes 0/0 to be 100% to make working with sorting and -%% weights simpler. -usage(0, 0) -> 1.00; -usage(0.0, 0.0) -> 1.00; -%usage(N,0) -> ???; -usage(Block, Carrier) -> Block / Carrier. - -%% Calculation for the average of blocks being used. -average_calc([]) -> - []; -average_calc([{{Instance, Type, count}, Ct}, {{Instance, Type, size}, Size} | Rest]) -> - case {Size, Ct} of - {_, 0} when Size == 0 -> [{Instance, Type, 0} | average_calc(Rest)]; - _ -> [{Instance, Type, Size / Ct} | average_calc(Rest)] - end. - -%% Regrouping/merging values together in proplists -average_group([]) -> []; -average_group([{Instance, Type1, N}, {Instance, Type2, M} | Rest]) -> - [{Instance, [{Type1, N}, {Type2, M}]} | average_group(Rest)]. - -%% Get the total carrier size -container_size(Props, Keyword, Container) -> - Sbcs = container_value(Props, Keyword, sbcs, Container), - Mbcs = container_value(Props, Keyword, mbcs, Container), - Sbcs + Mbcs. - -container_value(Props, Keyword, Type, Container) - when is_atom(Keyword) -> - container_value(Props, key2pos(Keyword), Type, Container); -container_value(Props, Pos, mbcs = Type, Container) - when Pos == ?CURRENT_POS, - ((Container =:= blocks) or (Container =:= blocks_size) - or (Container =:= carriers) or (Container =:= carriers_size)) -> - %% We include the mbcs_pool into the value for mbcs. - %% The mbcs_pool contains carriers that have been abandoned - %% by the specific allocator instance and can therefore be - %% grabbed by another instance of the same type. - %% The pool was added in R16B02 and enabled by default in 17.0. - %% See erts/emulator/internal_docs/CarrierMigration.md in - %% Erlang/OTP repo for more details. - Pool = case proplists:get_value(mbcs_pool, Props) of - PoolProps when PoolProps =/= undefined -> - element(Pos, lists:keyfind(Container, 1, PoolProps)); - _ -> 0 - end, - TypeProps = proplists:get_value(Type, Props), - Pool + element(Pos, lists:keyfind(Container, 1, TypeProps)); -container_value(Props, Pos, Type, Container) - when Type =:= sbcs; Type =:= mbcs -> - TypeProps = proplists:get_value(Type, Props), - element(Pos, lists:keyfind(Container, 1, TypeProps)). - -%% Create a new snapshot -snapshot_int() -> - {erlang:memory(), allocators()}. - -%% If no snapshot has been taken/loaded then we use current values -snapshot_get_int() -> - case snapshot_get() of - undefined -> - conv(snapshot_int()); - Snapshot -> - conv(Snapshot) - end. - -%% Get the alloc part of a snapshot -alloc() -> - {_Mem, Allocs} = snapshot_get_int(), - Allocs. -alloc(Type) -> - [{{T, Instance}, Props} || {{T, Instance}, Props} <- alloc(), - lists:member(T, Type)]. - -%% Get only alloc_util allocs -util_alloc() -> - alloc(?UTIL_ALLOCATORS). - -key2pos(current) -> - ?CURRENT_POS; -key2pos(max) -> - ?MAX_POS. diff --git a/src/test/recon-2.5.1/recon_lib.erl b/src/test/recon-2.5.1/recon_lib.erl deleted file mode 100644 index 8bb90b0..0000000 --- a/src/test/recon-2.5.1/recon_lib.erl +++ /dev/null @@ -1,285 +0,0 @@ -%%% @author Fred Hebert -%%% [http://ferd.ca/] -%%% @doc Regroups useful functionality used by recon when dealing with data -%%% from the node. The functions in this module allow quick runtime access -%%% to fancier behaviour than what would be done using recon module itself. -%%% @end --module(recon_lib). --export([sliding_window/2, sample/2, count/1, - port_list/1, port_list/2, - proc_attrs/1, proc_attrs/2, - inet_attrs/1, inet_attrs/2, - triple_to_pid/3, term_to_pid/1, - term_to_port/1, - time_map/5, time_fold/6, - scheduler_usage_diff/2, - sublist_top_n_attrs/2]). -%% private exports --export([binary_memory/1]). - --type diff() :: [recon:proc_attrs() | recon:inet_attrs()]. - -%% @doc Compare two samples and return a list based on some key. The type mentioned -%% for the structure is `diff()' (`{Key,Val,Other}'), which is compatible with -%% the {@link recon:proc_attrs()} type. --spec sliding_window(First :: diff(), Last :: diff()) -> diff(). -sliding_window(First, Last) -> - Dict = lists:foldl( - fun({Key, {Current, Other}}, Acc) -> - dict:update(Key, - fun({Old, _Other}) -> {Current - Old, Other} end, - {Current, Other}, - Acc) - end, - dict:from_list([{K, {V, O}} || {K, V, O} <- First]), - [{K, {V, O}} || {K, V, O} <- Last] - ), - [{K, V, O} || {K, {V, O}} <- dict:to_list(Dict)]. - -%% @doc Runs a fun once, waits `Ms', runs the fun again, -%% and returns both results. --spec sample(Ms :: non_neg_integer(), fun(() -> term())) -> - {First :: term(), Second :: term()}. -sample(Delay, Fun) -> - First = Fun(), - timer:sleep(Delay), - Second = Fun(), - {First, Second}. - -%% @doc Takes a list of terms, and counts how often each of -%% them appears in the list. The list returned is in no -%% particular order. --spec count([term()]) -> [{term(), Count :: integer()}]. -count(Terms) -> - Dict = lists:foldl( - fun(Val, Acc) -> dict:update_counter(Val, 1, Acc) end, - dict:new(), - Terms - ), - dict:to_list(Dict). - -%% @doc Returns a list of all the open ports in the VM, coupled with -%% one of the properties desired from `erlang:port_info/1-2'. --spec port_list(Attr :: atom()) -> [{port(), term()}]. -port_list(Attr) -> - [{Port, Val} || Port <- erlang:ports(), - {_, Val} <- [erlang:port_info(Port, Attr)]]. - -%% @doc Returns a list of all the open ports in the VM, but only -%% if the `Attr''s resulting value matches `Val'. `Attr' must be -%% a property accepted by `erlang:port_info/2'. --spec port_list(Attr :: atom(), term()) -> [port()]. -port_list(Attr, Val) -> - [Port || Port <- erlang:ports(), - {Attr, Val} =:= erlang:port_info(Port, Attr)]. - -%% @doc Returns the attributes ({@link recon:proc_attrs()}) of -%% all processes of the node, except the caller. --spec proc_attrs(term()) -> [recon:proc_attrs()]. -proc_attrs(AttrName) -> - Self = self(), - [Attrs || Pid <- processes(), - Pid =/= Self, - {ok, Attrs} <- [proc_attrs(AttrName, Pid)] - ]. - -%% @doc Returns the attributes of a given process. This form of attributes -%% is standard for most comparison functions for processes in recon. -%% -%% A special attribute is `binary_memory', which will reduce the memory used -%% by the process for binary data on the global heap. --spec proc_attrs(term(), pid()) -> {ok, recon:proc_attrs()} | {error, term()}. -proc_attrs(binary_memory, Pid) -> - case process_info(Pid, [binary, registered_name, - current_function, initial_call]) of - [{_, Bins}, {registered_name, Name}, Init, Cur] -> - {ok, {Pid, binary_memory(Bins), [Name || is_atom(Name)] ++ [Init, Cur]}}; - undefined -> - {error, undefined} - end; -proc_attrs(AttrName, Pid) -> - case process_info(Pid, [AttrName, registered_name, - current_function, initial_call]) of - [{_, Attr}, {registered_name, Name}, Init, Cur] -> - {ok, {Pid, Attr, [Name || is_atom(Name)] ++ [Init, Cur]}}; - undefined -> - {error, undefined} - end. - -%% @doc Returns the attributes ({@link recon:inet_attrs()}) of -%% all inet ports (UDP, SCTP, TCP) of the node. --spec inet_attrs(term()) -> [recon:inet_attrs()]. -inet_attrs(AttrName) -> - Ports = [Port || Port <- erlang:ports(), - {_, Name} <- [erlang:port_info(Port, name)], - Name =:= "tcp_inet" orelse - Name =:= "udp_inet" orelse - Name =:= "sctp_inet"], - [Attrs || Port <- Ports, - {ok, Attrs} <- [inet_attrs(AttrName, Port)]]. - -%% @doc Returns the attributes required for a given inet port (UDP, -%% SCTP, TCP). This form of attributes is standard for most comparison -%% functions for processes in recon. --spec inet_attrs(AttributeName, port()) -> {ok, recon:inet_attrs()} -| {error, term()} when - AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct' - | 'cnt' | 'oct'. -inet_attrs(Attr, Port) -> - Attrs = case Attr of - cnt -> [recv_cnt, send_cnt]; - oct -> [recv_oct, send_oct]; - _ -> [Attr] - end, - case inet:getstat(Port, Attrs) of - {ok, Props} -> - ValSum = lists:foldl(fun({_, X}, Y) -> X + Y end, 0, Props), - {ok, {Port, ValSum, Props}}; - {error, Reason} -> - {error, Reason} - end. - - -%% @doc Equivalent of `pid(X,Y,Z)' in the Erlang shell. --spec triple_to_pid(N, N, N) -> pid() when - N :: non_neg_integer(). -triple_to_pid(X, Y, Z) -> - list_to_pid("<" ++ integer_to_list(X) ++ "." ++ - integer_to_list(Y) ++ "." ++ - integer_to_list(Z) ++ ">"). - -%% @doc Transforms a given term to a pid. --spec term_to_pid(recon:pid_term()) -> pid(). -term_to_pid(Pid) when is_pid(Pid) -> Pid; -term_to_pid(Name) when is_atom(Name) -> whereis(Name); -term_to_pid(List = "<0." ++ _) -> list_to_pid(List); -term_to_pid(Binary = <<"<0.", _/binary>>) -> list_to_pid(binary_to_list(Binary)); -term_to_pid({global, Name}) -> global:whereis_name(Name); -term_to_pid({via, Module, Name}) -> Module:whereis_name(Name); -term_to_pid({X, Y, Z}) when is_integer(X), is_integer(Y), is_integer(Z) -> - triple_to_pid(X, Y, Z). - -%% @doc Transforms a given term to a port --spec term_to_port(recon:port_term()) -> port(). -term_to_port(Port) when is_port(Port) -> Port; -term_to_port(Name) when is_atom(Name) -> whereis(Name); -term_to_port("#Port<0." ++ Id) -> - N = list_to_integer(lists:sublist(Id, length(Id) - 1)), % drop trailing '>' - term_to_port(N); -term_to_port(N) when is_integer(N) -> - %% We rebuild the term from the int received: - %% http://www.erlang.org/doc/apps/erts/erl_ext_dist.html#id86892 - Name = iolist_to_binary(atom_to_list(node())), - NameLen = iolist_size(Name), - Vsn = binary:last(term_to_binary(self())), - Bin = <<131, % term encoding value - 102, % port tag - 100, % atom ext tag, used for node name - NameLen:2/unit:8, - Name:NameLen/binary, - N:4/unit:8, % actual counter value - Vsn:8>>, % version - binary_to_term(Bin). - -%% @doc Calls a given function every `Interval' milliseconds and supports -%% a map-like interface (each result is modified and returned) --spec time_map(N, Interval, Fun, State, MapFun) -> [term()] when - N :: non_neg_integer(), - Interval :: pos_integer(), - Fun :: fun((State) -> {term(), State}), - State :: term(), - MapFun :: fun((_) -> term()). -time_map(0, _, _, _, _) -> - []; -time_map(N, Interval, Fun, State, MapFun) -> - {Res, NewState} = Fun(State), - timer:sleep(Interval), - [MapFun(Res) | time_map(N - 1, Interval, Fun, NewState, MapFun)]. - -%% @doc Calls a given function every `Interval' milliseconds and supports -%% a fold-like interface (each result is modified and accumulated) --spec time_fold(N, Interval, Fun, State, FoldFun, Init) -> [term()] when - N :: non_neg_integer(), - Interval :: pos_integer(), - Fun :: fun((State) -> {term(), State}), - State :: term(), - FoldFun :: fun((term(), Init) -> Init), - Init :: term(). -time_fold(0, _, _, _, _, Acc) -> - Acc; -time_fold(N, Interval, Fun, State, FoldFun, Init) -> - timer:sleep(Interval), - {Res, NewState} = Fun(State), - Acc = FoldFun(Res, Init), - time_fold(N - 1, Interval, Fun, NewState, FoldFun, Acc). - -%% @doc Diffs two runs of erlang:statistics(scheduler_wall_time) and -%% returns usage metrics in terms of cores and 0..1 percentages. --spec scheduler_usage_diff(SchedTime, SchedTime) -> undefined | [{SchedulerId, Usage}] when - SchedTime :: [{SchedulerId, ActiveTime, TotalTime}], - SchedulerId :: pos_integer(), - Usage :: number(), - ActiveTime :: non_neg_integer(), - TotalTime :: non_neg_integer(). -scheduler_usage_diff(First, Last) when First =:= undefined orelse Last =:= undefined -> - undefined; -scheduler_usage_diff(First, Last) -> - lists:map( - fun({{I, _A0, T}, {I, _A1, T}}) -> {I, 0.0}; % Avoid divide by zero - ({{I, A0, T0}, {I, A1, T1}}) -> {I, (A1 - A0) / (T1 - T0)} - end, - lists:zip(lists:sort(First), lists:sort(Last)) - ). - -%% @doc Returns the top n element of a list of process or inet attributes --spec sublist_top_n_attrs([Attrs], pos_integer()) -> [Attrs] - when Attrs :: recon:proc_attrs() | recon:inet_attrs(). -sublist_top_n_attrs(_, 0) -> - %% matching lists:sublist/2 behaviour - []; -sublist_top_n_attrs(List, Len) -> - pheap_fill(List, Len, []). - -%% @private crush binaries from process_info into their amount of place -%% taken in memory. -binary_memory(Bins) -> - lists:foldl(fun({_, Mem, _}, Tot) -> Mem + Tot end, 0, Bins). - -%%%%%%%%%%%%%%% -%%% PRIVATE %%% -%%%%%%%%%%%%%%% -pheap_fill(List, 0, Heap) -> - pheap_full(List, Heap); -pheap_fill([], _, Heap) -> - pheap_to_list(Heap, []); -pheap_fill([{Y, X, _} = H | T], N, Heap) -> - pheap_fill(T, N - 1, insert({{X, Y}, H}, Heap)). - -pheap_full([], Heap) -> - pheap_to_list(Heap, []); -pheap_full([{Y, X, _} = H | T], [{K, _} | HeapT] = Heap) -> - case {X, Y} of - N when N > K -> - pheap_full(T, insert({N, H}, merge_pairs(HeapT))); - _ -> - pheap_full(T, Heap) - end. - -pheap_to_list([], Acc) -> Acc; -pheap_to_list([{_, H} | T], Acc) -> - pheap_to_list(merge_pairs(T), [H | Acc]). - --compile({inline, [insert/2, merge/2]}). -insert(E, []) -> [E]; %% merge([E], H) -insert(E, [E2 | _] = H) when E =< E2 -> [E, H]; -insert(E, [E2 | H]) -> [E2, [E] | H]. - -merge(H1, []) -> H1; -merge([E1 | H1], [E2 | _] = H2) when E1 =< E2 -> [E1, H2 | H1]; -merge(H1, [E2 | H2]) -> [E2, H1 | H2]. - -merge_pairs([]) -> []; -merge_pairs([H]) -> H; -merge_pairs([A, B | T]) -> merge(merge(A, B), merge_pairs(T)). - - diff --git a/src/test/recon-2.5.1/recon_map.erl b/src/test/recon-2.5.1/recon_map.erl deleted file mode 100644 index 2235846..0000000 --- a/src/test/recon-2.5.1/recon_map.erl +++ /dev/null @@ -1,208 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @author bartlomiej.gorny@erlang-solutions.com -%%% @doc -%%% This module handles formatting maps. -%% It allows for trimming output to selected fields, or to nothing at all. It also adds a label -%% to a printout. -%% To set up a limit for a map, you need to give recon a way to tell the map you want to -%% trim from all the other maps, so you have to provide something like a 'type definition'. -%% It can be either another map which is compared to the arg, or a fun. -%%% @end -%%%------------------------------------------------------------------- --module(recon_map). --author("bartlomiej.gorny@erlang-solutions.com"). -%% API - --export([limit/3, list/0, is_active/0, clear/0, remove/1, rename/2]). --export([process_map/1]). - --type map_label() :: atom(). --type pattern() :: map() | function(). --type limit() :: all | none | atom() | binary() | [any()]. - -%% @doc quickly check if we want to do any record formatting --spec is_active() -> boolean(). -is_active() -> - case whereis(recon_ets_maps) of - undefined -> false; - _ -> true - end. - -%% @doc remove all imported definitions, destroy the table, clean up -clear() -> - maybe_kill(recon_ets_maps), - ok. - -%% @doc Limit output to selected keys of a map (can be 'none', 'all', a key or a list of keys). -%% Pattern selects maps to process: a "pattern" is just a map, and if all key/value pairs of a pattern -%% are present in a map (in other words, the pattern is a subset), then we say the map matches -%% and we process it accordingly (apply the limit). -%% -%% Patterns are applied in alphabetical order, until a match is found. -%% -%% Instead of a pattern you can also provide a function which will take a map and return a boolean. -%% @end --spec limit(map_label(), pattern(), limit()) -> ok | {error, any()}. -limit(Label, #{} = Pattern, Limit) when is_atom(Label) -> - store_pattern(Label, Pattern, Limit); -limit(Label, Pattern, Limit) when is_atom(Label), is_function(Pattern) -> - store_pattern(Label, Pattern, Limit). - -%% @doc prints out all "known" map definitions and their limit settings. -%% Printout tells a map's name, the matching fields required, and the limit options. -%% @end -list() -> - ensure_table_exists(), - io:format("~nmap definitions and limits:~n"), - list(ets:tab2list(patterns_table_name())). - -%% @doc remove a given map entry --spec remove(map_label()) -> true. -remove(Label) -> - ensure_table_exists(), - ets:delete(patterns_table_name(), Label). - -%% @doc rename a given map entry, which allows to to change priorities for -%% matching. The first argument is the current name, and the second -%% argument is the new name. --spec rename(map_label(), map_label()) -> renamed | missing. -rename(Name, NewName) -> - ensure_table_exists(), - case ets:lookup(patterns_table_name(), Name) of - [{Name, Pattern, Limit}] -> - ets:insert(patterns_table_name(), {NewName, Pattern, Limit}), - ets:delete(patterns_table_name(), Name), - renamed; - [] -> - missing - end. - -%% @doc prints out all "known" map filter definitions and their settings. -%% Printout tells the map's label, the matching patterns, and the limit options -%% @end -list([]) -> - io:format("~n"), - ok; -list([{Label, Pattern, Limit} | Rest]) -> - io:format("~p: ~p -> ~p~n", [Label, Pattern, Limit]), - list(Rest). - -%% @private given a map, scans saved patterns for one that matches; if found, returns a label -%% and a map with limits applied; otherwise returns 'none' and original map. -%% Pattern can be: -%%
    -%%
  • a map - then each key in pattern is checked for equality with the map in question
  • -%%
  • a fun(map()) -> boolean()
  • -%%
--spec process_map(map()) -> map() | {atom(), map()}. -process_map(M) -> - process_map(M, ets:tab2list(patterns_table_name())). - -process_map(M, []) -> - M; -process_map(M, [{Label, Pattern, Limit} | Rest]) -> - case map_matches(M, Pattern) of - true -> - {Label, apply_map_limits(Limit, M)}; - false -> - process_map(M, Rest) - end. - -map_matches(#{} = M, Pattern) when is_function(Pattern) -> - Pattern(M); -map_matches(_, []) -> - true; -map_matches(M, [{K, V} | Rest]) -> - case maps:is_key(K, M) of - true -> - case maps:get(K, M) of - V -> - map_matches(M, Rest); - _ -> - false - end; - false -> - false - end. - -apply_map_limits(none, M) -> - M; -apply_map_limits(all, _) -> - #{}; -apply_map_limits(Fields, M) -> - maps:with(Fields, M). - -patterns_table_name() -> recon_map_patterns. - -store_pattern(Label, Pattern, Limit) -> - ensure_table_exists(), - ets:insert(patterns_table_name(), {Label, prepare_pattern(Pattern), prepare_limit(Limit)}), - ok. - -prepare_limit(all) -> all; -prepare_limit(none) -> none; -prepare_limit(Limit) when is_binary(Limit) -> [Limit]; -prepare_limit(Limit) when is_atom(Limit) -> [Limit]; -prepare_limit(Limit) when is_list(Limit) -> Limit. - -prepare_pattern(Pattern) when is_function(Pattern) -> Pattern; -prepare_pattern(Pattern) when is_map(Pattern) -> maps:to_list(Pattern). - - -ensure_table_exists() -> - case ets:info(patterns_table_name()) of - undefined -> - case whereis(recon_ets_maps) of - undefined -> - Parent = self(), - Ref = make_ref(), - %% attach to the currently running session - {Pid, MonRef} = spawn_monitor(fun() -> - register(recon_ets_maps, self()), - ets:new(patterns_table_name(), [ordered_set, public, named_table]), - Parent ! Ref, - ets_keeper() - end), - receive - Ref -> - erlang:demonitor(MonRef, [flush]), - Pid; - {'DOWN', MonRef, _, _, Reason} -> - error(Reason) - end; - Pid -> - Pid - end; - Pid -> - Pid - end. - -ets_keeper() -> - receive - stop -> ok; - _ -> ets_keeper() - end. - -%%%%%%%%%%%%%%% -%%% HELPERS %%% -%%%%%%%%%%%%%%% - -maybe_kill(Name) -> - case whereis(Name) of - undefined -> - ok; - Pid -> - unlink(Pid), - exit(Pid, kill), - wait_for_death(Pid, Name) - end. - -wait_for_death(Pid, Name) -> - case is_process_alive(Pid) orelse whereis(Name) =:= Pid of - true -> - timer:sleep(10), - wait_for_death(Pid, Name); - false -> - ok - end. - diff --git a/src/test/recon-2.5.1/recon_rec.erl b/src/test/recon-2.5.1/recon_rec.erl deleted file mode 100644 index f48615e..0000000 --- a/src/test/recon-2.5.1/recon_rec.erl +++ /dev/null @@ -1,279 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @author bartlomiej.gorny@erlang-solutions.com -%%% @doc -%%% This module handles formatting records for known record types. -%%% Record definitions are imported from modules by user. Definitions are -%%% distinguished by record name and its arity, if you have multiple records -%%% of the same name and size, you have to choose one of them and some of your -%%% records may be wrongly labelled. You can manipulate your definition list by -%%% using import/1 and clear/1, and check which definitions are in use by executing -%%% list/0. -%%% @end -%%%------------------------------------------------------------------- --module(recon_rec). --author("bartlomiej.gorny@erlang-solutions.com"). -%% API - --export([is_active/0]). --export([import/1, clear/1, clear/0, list/0, get_list/0, limit/3]). --export([format_tuple/1]). - --ifdef(TEST). --export([lookup_record/2]). --endif. - -% basic types --type field() :: atom(). --type record_name() :: atom(). -% compound --type limit() :: all | none | field() | [field()]. --type listentry() :: {module(), record_name(), [field()], limit()}. --type import_result() :: {imported, module(), record_name(), arity()} -| {overwritten, module(), record_name(), arity()} -| {ignored, module(), record_name(), arity(), module()}. - -%% @doc import record definitions from a module. If a record definition of the same name -%% and arity has already been imported from another module then the new -%% definition is ignored (returned info tells you from which module the existing definition was imported). -%% You have to choose one and possibly remove the old one using -%% clear/1. Supports importing multiple modules at once (by giving a list of atoms as -%% an argument). -%% @end --spec import(module() | [module()]) -> import_result() | [import_result()]. -import(Modules) when is_list(Modules) -> - lists:foldl(fun import/2, [], Modules); -import(Module) -> - import(Module, []). - -%% @doc quickly check if we want to do any record formatting --spec is_active() -> boolean(). -is_active() -> - case whereis(recon_ets) of - undefined -> false; - _ -> true - end. - -%% @doc remove definitions imported from a module. -clear(Module) -> - lists:map(fun(R) -> rem_for_module(R, Module) end, ets:tab2list(records_table_name())). - -%% @doc remove all imported definitions, destroy the table, clean up -clear() -> - maybe_kill(recon_ets), - ok. - -%% @doc prints out all "known" (imported) record definitions and their limit settings. -%% Printout tells module a record originates from, its name and a list of field names, -%% plus the record's arity (may be handy if handling big records) and a list of field it -%% limits its output to, if set. -%% @end -list() -> - F = fun({Module, Name, Fields, Limits}) -> - Fnames = lists:map(fun atom_to_list/1, Fields), - Flds = join(",", Fnames), - io:format("~p: #~p(~p){~s} ~p~n", - [Module, Name, length(Fields), Flds, Limits]) - end, - io:format("Module: #Name(Size){} Limits~n==========~n", []), - lists:foreach(F, get_list()). - -%% @doc returns a list of active record definitions --spec get_list() -> [listentry()]. -get_list() -> - ensure_table_exists(), - Lst = lists:map(fun make_list_entry/1, ets:tab2list(records_table_name())), - lists:sort(Lst). - -%% @doc Limit output to selected fields of a record (can be 'none', 'all', a field or a list of fields). -%% Limit set to 'none' means there is no limit, and all fields are displayed; limit 'all' means that -%% all fields are squashed and only record name will be shown. -%% @end --spec limit(record_name(), arity(), limit()) -> ok | {error, any()}. -limit(Name, Arity, Limit) when is_atom(Name), is_integer(Arity) -> - case lookup_record(Name, Arity) of - [] -> - {error, record_unknown}; - [{Key, Fields, Mod, _}] -> - ets:insert(records_table_name(), {Key, Fields, Mod, Limit}), - ok - end. - -%% @private if a tuple is a known record, formats is as "#recname{field=value}", otherwise returns -%% just a printout of a tuple. -format_tuple(Tuple) -> - ensure_table_exists(), - First = element(1, Tuple), - format_tuple(First, Tuple). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% PRIVATE -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -make_list_entry({{Name, _}, Fields, Module, Limits}) -> - FmtLimit = case Limits of - [] -> none; - Other -> Other - end, - {Module, Name, Fields, FmtLimit}. - -import(Module, ResultList) -> - ensure_table_exists(), - lists:foldl(fun(Rec, Res) -> store_record(Rec, Module, Res) end, - ResultList, - get_record_defs(Module)). - -store_record(Rec, Module, ResultList) -> - {Name, Fields} = Rec, - Arity = length(Fields), - Result = case lookup_record(Name, Arity) of - [] -> - ets:insert(records_table_name(), rec_info(Rec, Module)), - {imported, Module, Name, Arity}; - [{_, _, Module, _}] -> - ets:insert(records_table_name(), rec_info(Rec, Module)), - {overwritten, Module, Name, Arity}; - [{_, _, Mod, _}] -> - {ignored, Module, Name, Arity, Mod} - end, - [Result | ResultList]. - -get_record_defs(Module) -> - Path = code:which(Module), - {ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(Path, [abstract_code]), - lists:foldl(fun get_record/2, [], AC). - -get_record({attribute, _, record, Rec}, Acc) -> [Rec | Acc]; -get_record(_, Acc) -> Acc. - -%% @private -lookup_record(RecName, FieldCount) -> - ensure_table_exists(), - ets:lookup(records_table_name(), {RecName, FieldCount}). - -%% @private -ensure_table_exists() -> - case ets:info(records_table_name()) of - undefined -> - case whereis(recon_ets) of - undefined -> - Parent = self(), - Ref = make_ref(), - %% attach to the currently running session - {Pid, MonRef} = spawn_monitor(fun() -> - register(recon_ets, self()), - ets:new(records_table_name(), [set, public, named_table]), - Parent ! Ref, - ets_keeper() - end), - receive - Ref -> - erlang:demonitor(MonRef, [flush]), - Pid; - {'DOWN', MonRef, _, _, Reason} -> - error(Reason) - end; - Pid -> - Pid - end; - Pid -> - Pid - end. - -records_table_name() -> recon_record_definitions. - -rec_info({Name, Fields}, Module) -> - {{Name, length(Fields)}, field_names(Fields), Module, none}. - -rem_for_module({_, _, Module, _} = Rec, Module) -> - ets:delete_object(records_table_name(), Rec); -rem_for_module(_, _) -> - ok. - -ets_keeper() -> - receive - stop -> ok; - _ -> ets_keeper() - end. - -field_names(Fields) -> - lists:map(fun field_name/1, Fields). - -field_name({record_field, _, {atom, _, Name}}) -> Name; -field_name({record_field, _, {atom, _, Name}, _Default}) -> Name; -field_name({typed_record_field, Field, _Type}) -> field_name(Field). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% FORMATTER -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -format_tuple(Name, Rec) when is_atom(Name) -> - case lookup_record(Name, size(Rec) - 1) of - [RecDef] -> format_record(Rec, RecDef); - _ -> - List = tuple_to_list(Rec), - ["{", join(", ", [recon_trace:format_trace_output(true, El) || El <- List]), "}"] - end; -format_tuple(_, Tuple) -> - format_default(Tuple). - -format_default(Val) -> - io_lib:format("~p", [Val]). - -format_record(Rec, {{Name, Arity}, Fields, _, Limits}) -> - ExpectedLength = Arity + 1, - case tuple_size(Rec) of - ExpectedLength -> - [_ | Values] = tuple_to_list(Rec), - List = lists:zip(Fields, Values), - LimitedList = apply_limits(List, Limits), - ["#", atom_to_list(Name), "{", - join(", ", [format_kv(Key, Val) || {Key, Val} <- LimitedList]), - "}"]; - _ -> - format_default(Rec) - end. - -format_kv(Key, Val) -> - %% Some messy mutually recursive calls we can't avoid - [recon_trace:format_trace_output(true, Key), "=", recon_trace:format_trace_output(true, Val)]. - -apply_limits(List, none) -> List; -apply_limits(_List, all) -> []; -apply_limits(List, Field) when is_atom(Field) -> - [{Field, proplists:get_value(Field, List)}, {more, '...'}]; -apply_limits(List, Limits) -> - lists:filter(fun({K, _}) -> lists:member(K, Limits) end, List) ++ [{more, '...'}]. - -%%%%%%%%%%%%%%% -%%% HELPERS %%% -%%%%%%%%%%%%%%% - -maybe_kill(Name) -> - case whereis(Name) of - undefined -> - ok; - Pid -> - unlink(Pid), - exit(Pid, kill), - wait_for_death(Pid, Name) - end. - -wait_for_death(Pid, Name) -> - case is_process_alive(Pid) orelse whereis(Name) =:= Pid of - true -> - timer:sleep(10), - wait_for_death(Pid, Name); - false -> - ok - end. - --ifdef(OTP_RELEASE). --spec join(term(), [term()]) -> [term()]. -join(Sep, List) -> - lists:join(Sep, List). --else. --spec join(string(), [string()]) -> string(). -join(Sep, List) -> - string:join(List, Sep). --endif. diff --git a/src/test/recon-2.5.1/recon_test.erl b/src/test/recon-2.5.1/recon_test.erl deleted file mode 100644 index 6138caa..0000000 --- a/src/test/recon-2.5.1/recon_test.erl +++ /dev/null @@ -1,539 +0,0 @@ --module(recon_test). - --compile([export_all, nowarn_export_all]). - --define(MEM_INFO_INIT(), memInfoInit(?MODULE, ?LINE)). --define(MEM_INFO_PRINT(), memInfoPrint(?MODULE, ?LINE, 100)). --define(MEM_INFO_PRINT(Threshold), memInfoPrint(?MODULE, ?LINE, Threshold)). - --define(pdMemInfo, '$pdMemInfo'). -memInfoInit(CurModule, CurLine) -> - erlang:put(?pdMemInfo, {CurModule, CurLine, erlang:system_time(nanosecond), recon:info(self(), memory_used), erlang:memory()}). - -memInfoPrint(CurModule, CurLine, Threshold) -> - case erlang:get(?pdMemInfo) of - undefined -> - erlang:put(?pdMemInfo, {CurModule, CurLine, erlang:system_time(nanosecond), recon:info(self(), memory_used), erlang:memory()}); - {OldModule, OldLine, OldTime, OldMemInfo, OldSumInfo} -> - CurMemInfo = recon:info(self(), memory_used), - CurTime = erlang:system_time(nanosecond), - CurSumInfo = erlang:memory(), - erlang:put(?pdMemInfo, {CurModule, CurLine, CurTime, CurMemInfo, CurSumInfo}), - SubPid = element(2, lists:nth(1, element(2, CurMemInfo))) - element(2, lists:nth(1, element(2, OldMemInfo))), - SubSum = element(2, lists:keyfind(total, 1, CurSumInfo)) - element(2, lists:keyfind(total, 1, OldSumInfo)), - case erlang:abs(SubSum) >= Threshold orelse erlang:abs(SubPid) >= Threshold of - true -> - io:format( - "IMY*********Memory use changes are too large:~n" - "addOrSubSum:~20w~n" - "addOrSubPid:~20w~n" - "usedTimeDiff:~19w~n" - "oldLine:~w~n" - "CurLine:~w~n" - "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n" - "OldSumInfo:~w~n" - "CurSumInfo:~w~n" - "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n" - "OldPidInfo:~w~n" - "************************************************************************************~n" - "CurPidInfo:~w~n", [SubSum, SubPid, CurTime - OldTime, {old, OldModule, OldLine, OldTime}, {cur, CurModule, CurLine, CurTime}, OldSumInfo, CurSumInfo, OldMemInfo, CurMemInfo]); - _ -> - ignore - end - end, - gc(). - -get_test() -> - #{'$map_tab' => tb_client, black_hole => 6, bomb_drill => 7, cat_coin => 680, - client_data => <<"{\"db_base\":{\"uid\":1006,\"talking_scene_info\":[],\"talking_data\":[]},\"db_item_list\":[[\"item_uid\",\"item_tid\",\"item_num\"],[1,1001,1006,4],[2,1002,1007,4],[3,1003,1008,4],[4,1004,1015,4],[5,1005,1016,4],[6,1006,1017,4]],\"db_inland_idip\":{\"coin_before_count\":0,\"coin_after_count\":0,\"coin_freeze_info\":0,\"coin_opt_id\":0,\"band_join_ranking_info\":[]},\"db_cat_feed\":{\"money_time\":0,\"shovel\":0,\"day_index\":0,\"accum_food_time\":0,\"money_list\":[],\"clean_frd_time\":[],\"accum_water_time\":0,\"accum_money\":0,\"water_time\":0,\"remain_water\":0,\"remain_money\":0,\"food_time\":0,\"remain_food\":0,\"first_add_money_time\":0,\"used_shovel\":0,\"last_add_day\":0},\"db_store_ad\":{\"watched_count\":0,\"next_ad_time\":0,\"today_cookie\":\"\"},\"db_level\":{\"level_item_list\":[],\"eng_infinite_tag\":0,\"flag_lv\":0,\"lv_type\":0,\"level_buy_time\":0,\"continue_cost\":0,\"activity_form\":\"\",\"lv_failn_advice\":0,\"level_state\":0,\"set_result\":0,\"use_item_list\":[],\"lv_end_gold\":false,\"lv_failed_count\":[]},\"db_data_verify\":{\"saved_index\":0,\"generate_index\":0,\"server_data_version_code\":0,\"upload_data_time\":0},\"db_timeline_stuff_style\":[],\"db_cat_map\":[],\"db_finished_activity\":[],\"db_monthcard\":{\"have_got\":false,\"day_left_os_time\":0,\"week_day\":0,\"disc_num\":0,\"card_day\":0,\"left_sec\":0,\"card_id\":0,\"day_left_time\":0,\"tag\":0,\"left_os_time\":0,\"show_monthcard_cell\":[]},\"db_logger\":{\"online_sec\":0,\"coin_cost\":0,\"use_doublerocket\":0,\"use_crosshammer\":0,\"use_bombanddril\":0,\"seed\":0,\"day_idx\":0,\"log\":0,\"use_glove\":0,\"gold_cost\":0,\"use_hammer\":0,\"use_blackhole\":0,\"day_num\":0,\"pay_num\":0,\"reshuffle\":0},\"db_role\":{\"order_id_list\":[],\"dr_infinite_left_sec\":0,\"lv_tag\":1,\"first_tlv\":true,\"login_time\":1608963650,\"energy\":5,\"lv_idx\":1,\"gold\":1000,\"login_day_count\":0,\"have_sign\":[],\"chapter\":1,\"energy_time\":0,\"spend_money\":0,\"gold_re\":0,\"bh_infinite_left_sec\":0,\"main_decorate_stuff\":0,\"server_mail_id\":0,\"token\":0,\"energy_recover_halve\":0,\"self_room_stars\":0,\"ad_remaining_num\":0,\"level\":1,\"eneryg_infinite_left_times\":0,\"bd_infinite_left_sec\":0,\"max_product_id\":0,\"play_time_sec\":0,\"last_login_day_index\":0,\"dr_infinite\":0,\"star\":0,\"bh_infinite\":0,\"cat_coin\":0,\"newplayer_signin_complete\":0,\"server_day_idx\":0,\"fb_first_award\":false,\"logout_time\":1608963650,\"special_point\":0,\"tutorial_on\":true,\"diamond\":0,\"energy_infinite\":0,\"role_name\":\"\",\"bd_infinite\":0},\"db_cat_room_map\":[],\"db_chapter_map\":[[\"chapter\",\"begin\",\"award_idx\",\"stuffs\"],{1:1,2:1,3:false,4:0,5:[]}],\"db_activity_settle\":{\"infinite_challenge_info\":[0,0,0]},\"db_cat_gift\":{\"max_award_num\":false,\"award_num\":false,\"end_ti\":0,\"first_feed_gift\":false,\"select_hour_type\":false,\"left_sec\":0,\"awards\":false,\"start_ti\":0},\"db_cat_stuff_map\":[],\"db_cat_gift_new\":{\"cur_gift\":[],\"is_unlock\":false,\"gifts\":[]},\"db_activity\":[],\"db_cat_ad\":{\"ad_num\":0,\"is_unlock\":false,\"cd_time\":0},\"db_clothes_out_cat\":[],\"db_award_map\":[],\"db_tutorial\":[],\"db_stuff_timeline\":[],\"db_inland_server\":{\"account_channel\":\"\",\"uuid\":\"\",\"channel_role\":[],\"bind_account_award\":false,\"decorate_progress\":0,\"story_progress\":0},\"db_cat_extra\":{\"unlock_list\":[]},\"db_mail_list\":[],\"db_stuff_child_visible\":[],\"db_head\":{\"show_head\":\"1\",\"own_head\":[]},\"db_cat_event\":{\"is_init\":false,\"start_time\":0,\"is_unlock\":false,\"num\":0},\"db_talking_choose\":[],\"db_process\":{\"process\":[]}}">>, - client_idx => 170, - client_incr => - [ - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":471.66666666667,\"food_time\":1608965030}},\"db_award_map\":{\"d\":[1077]},\"db_item_list\":{\"s\":[{\"m\":{\"item_num\":7}},{\"m\":{\"item_num\":6}},{\"m\":{\"item_num\":5}},{\"m\":{\"item_num\":5}}]},\"db_data_verify\":{\"m\":{\"generate_index\":212}},\"db_activity\":{\"s\":{5101:{\"m\":{\"_left_sec\":292567}},4001:{\"m\":{\"_left_sec\":292567}},5901:{\"m\":{\"_left_sec\":284462167}}}},\"db_role\":{\"m\":{\"energy_infinite\":1608979053,\"eneryg_infinite_left_times\":14019,\"logout_time\":1608965030,\"play_time_sec\":1380}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1080}},\"db_activity\":{\"s\":{5101:{\"m\":{\"_start_ti\":1608965020,\"_left_sec\":292574}},4001:{\"m\":{\"_start_ti\":1608965020,\"_left_sec\":292574}},5901:{\"m\":{\"_start_ti\":1608965020,\"_left_sec\":284462174}}},\"d\":[2001]},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":211}},\"db_finished_activity\":{\"m\":{2001:true}},\"db_role\":{\"m\":{\"gold\":6416}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1079}},\"db_award_map\":{\"m\":{1077:{\"item_award_index\":[[2089,[1,2,3,4,5]]],\"item_num\":1,\"goods_id\":0,\"reason\":13,\"cost_num\":0,\"uid\":1077,\"opt_info\":[2001,3],\"awards\":[[2024,120],[1006,1],[1007,1],[1008,1],[1015,1]],\"cost_currency\":0}}},\"db_data_verify\":{\"m\":{\"generate_index\":210}},\"db_activity\":{\"s\":{2001:{\"m\":{\"level_point_got\":12}}}},\"db_item_list\":{\"m\":{7:{\"item_uid\":1079,\"item_tid\":1019,\"item_num\":1}}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":209}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":208}},\"db_activity\":{\"s\":{5101:{\"m\":{\"score\":2}}}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":207}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":206}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":205}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1076}},\"db_data_verify\":{\"m\":{\"generate_index\":204}},\"db_role\":{\"m\":{\"star\":1,\"cat_coin\":680,\"gold\":6393}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":769916928}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":31}},\"db_data_verify\":{\"m\":{\"generate_index\":203}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206181}},5101:{\"m\":{\"_left_sec\":292581}},4001:{\"m\":{\"_left_sec\":292581}},5901:{\"m\":{\"_left_sec\":284462181}}}},\"db_role\":{\"m\":{\"logout_time\":1608965020,\"level\":32,\"lv_tag\":32,\"lv_idx\":32,\"play_time_sec\":1370}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20122:[],1071:[]}}}},\"db_talking_choose\":{\"m\":{\"20008-1\":true}},\"db_tutorial\":{\"m\":{138:true}},\"db_inland_server\":{\"m\":{\"decorate_progress\":23,\"story_progress\":37}},\"db_cat_gift_new\":{\"s\":{\"cur_gift\":{\"m\":{\"gift_id\":8022,\"end_time\":1608993795}},\"gifts\":{\"s\":[{\"m\":{\"index\":2,\"gift_id\":8020}},{\"m\":{\"index\":3,\"gift_id\":8021}}],\"d\":[3]}}},\"db_data_verify\":{\"m\":{\"generate_index\":202}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206191}},5101:{\"m\":{\"_left_sec\":292591}},4001:{\"m\":{\"_left_sec\":292591}},5901:{\"m\":{\"_left_sec\":284462191}}}},\"db_role\":{\"m\":{\"logout_time\":1608965000,\"main_decorate_stuff\":212,\"play_time_sec\":1350}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":472.66666666667,\"food_time\":1608964970}},\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{212:{\"stuff_id\":212,\"select_id\":1}}}}}}},\"db_data_verify\":{\"m\":{\"generate_index\":201}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206221}},5101:{\"m\":{\"_left_sec\":292621}},4001:{\"m\":{\"_left_sec\":292621}},5901:{\"m\":{\"_left_sec\":284462221}}}},\"db_role\":{\"m\":{\"logout_time\":1608964970,\"star\":0,\"play_time_sec\":1320}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1075}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":200}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964959,\"_left_sec\":206232,\"level_point_got\":11}},5101:{\"m\":{\"_start_ti\":1608964959,\"_left_sec\":292632}},4001:{\"m\":{\"_start_ti\":1608964959,\"_left_sec\":292632}},5901:{\"m\":{\"_start_ti\":1608964959,\"_left_sec\":284462232}}}},\"db_role\":{\"m\":{\"logout_time\":1608964960,\"play_time_sec\":1310,\"gold\":6343}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":199}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":198}},\"db_activity\":{\"s\":{5101:{\"m\":{\"score\":1}}}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":197}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":196}},\"db_activity\":{\"s\":{4001:{\"s\":{\"materials\":{\"m\":{132422520:40}}}}}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":195}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1074}},\"db_data_verify\":{\"m\":{\"generate_index\":194}},\"db_role\":{\"m\":{\"star\":2,\"cat_coin\":660,\"gold\":6317}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":1921384448}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":30}},\"db_data_verify\":{\"m\":{\"generate_index\":193}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206242}},5101:{\"m\":{\"_left_sec\":292642}},4001:{\"m\":{\"_left_sec\":292642}},5901:{\"m\":{\"_left_sec\":284462242}}}},\"db_role\":{\"m\":{\"level\":31,\"lv_tag\":31,\"lv_idx\":31}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{1039:[]}}}},\"db_inland_server\":{\"m\":{\"story_progress\":35}},\"db_tutorial\":{\"m\":{125:true}},\"db_data_verify\":{\"m\":{\"generate_index\":192}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206249}},5101:{\"m\":{\"_end_ti\":1609257600,\"_left_sec\":292649}},4001:{\"m\":{\"_left_sec\":292649}},5901:{\"m\":{\"_left_sec\":284462249}}}},\"db_role\":{\"m\":{\"logout_time\":1608964950,\"play_time_sec\":1300}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":191}},\"db_activity\":{\"s\":{4001:{\"m\":{\"_left_sec\":292656}},5901:{\"m\":{\"_left_sec\":284462256}},2001:{\"m\":{\"_left_sec\":206256}}},\"m\":{5101:{\"fb_start\":true,\"progress\":0,\"_start_ti\":1608964944,\"_end_ti\":1609257626,\"score\":0,\"_left_sec\":292682}}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1073}},\"db_data_verify\":{\"m\":{\"generate_index\":190}},\"db_activity\":{\"s\":{4001:{\"s\":{\"order_list\":{\"s\":[{\"s\":{\"ice_cream_list\":{\"d\":[1]}},\"m\":{\"end_time\":1608966135,\"id\":2001}}]}},\"m\":{\"_left_sec\":292659,\"interval\":0,\"progress\":1}},5901:{\"m\":{\"_left_sec\":284462259}},2001:{\"m\":{\"_left_sec\":206259}}}},\"db_role\":{\"m\":{\"logout_time\":1608964940,\"play_time_sec\":1290,\"gold\":6267}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":189}},\"db_activity\":{\"s\":{4001:{\"s\":{\"materials\":{\"m\":{-884040730:0}},\"order_list\":{\"s\":[{\"s\":{\"ice_cream_list\":{\"m\":[1]}}}]}},\"m\":{\"_left_sec\":292666}},5901:{\"m\":{\"_left_sec\":284462266}},2001:{\"m\":{\"_left_sec\":206266}}}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{1020:[]}}},\"m\":{\"uid\":1072}},\"db_inland_server\":{\"m\":{\"story_progress\":34}},\"db_tutorial\":{\"m\":{122:true}},\"db_data_verify\":{\"m\":{\"generate_index\":188}},\"db_activity\":{\"s\":{4001:{\"s\":{\"materials\":{\"m\":{-884040730:120}}},\"m\":{\"_end_ti\":1609257600,\"_left_sec\":292669}},5901:{\"m\":{\"_left_sec\":284462269}},2001:{\"m\":{\"_left_sec\":206269}}}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":187}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206270}},5901:{\"m\":{\"_left_sec\":284462270}}},\"m\":{4001:{\"_end_ti\":1609257612,\"progress_award\":[],\"order_list\":[{\"id\":1001,\"ice_cream_list\":[]},{\"id\":1006,\"ice_cream_list\":[]},{\"id\":1004,\"ice_cream_list\":[]}],\"_left_sec\":292682,\"_start_ti\":1608964930,\"materials\":{132422520:0,-884040730:0,-1047817473:0},\"fb_start\":true,\"interval\":5,\"progress\":0}}},\"db_role\":{\"m\":{\"logout_time\":1608964930,\"play_time_sec\":1280}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1071}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":186}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964918,\"_left_sec\":206278,\"level_point_got\":10}},5901:{\"m\":{\"_start_ti\":1608964918,\"_left_sec\":284462278}}}},\"db_role\":{\"m\":{\"logout_time\":1608964920,\"play_time_sec\":1270,\"gold\":6262}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":185}},\"db_cat_gift_new\":{\"s\":{\"gifts\":{\"m\":[{\"index\":1,\"cat_id\":0,\"gift_id\":8022},{\"index\":2,\"cat_id\":0,\"gift_id\":8020},{\"index\":3,\"cat_id\":0,\"gift_id\":8021}]}},\"m\":{\"is_unlock\":true}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1070}},\"db_data_verify\":{\"m\":{\"generate_index\":184}},\"db_role\":{\"m\":{\"star\":1,\"cat_coin\":640,\"gold\":6230}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":473.66666666667,\"food_time\":1608964910}},\"db_logger\":{\"m\":{\"seed\":843841536}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":29}},\"db_data_verify\":{\"m\":{\"generate_index\":183}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206284}},5901:{\"m\":{\"_left_sec\":284462284}}}},\"db_role\":{\"m\":{\"logout_time\":1608964910,\"level\":30,\"lv_tag\":30,\"lv_idx\":30,\"play_time_sec\":1260}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20112:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":22,\"story_progress\":33}},\"db_data_verify\":{\"m\":{\"generate_index\":182}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206290}},5901:{\"m\":{\"_left_sec\":284462290}}}},\"db_role\":{\"m\":{\"main_decorate_stuff\":211}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{211:{\"stuff_id\":211,\"select_id\":1}}}}}}},\"db_data_verify\":{\"m\":{\"generate_index\":181}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206296}},5901:{\"m\":{\"_left_sec\":284462296}}}},\"db_role\":{\"m\":{\"logout_time\":1608964900,\"star\":0,\"play_time_sec\":1250}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1069}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":180}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964886,\"_left_sec\":206310,\"level_point_got\":9,\"_end_ti\":1609171200}},5901:{\"m\":{\"_end_ti\":1893427200,\"_left_sec\":284462310,\"_start_ti\":1608964886}}}},\"db_role\":{\"m\":{\"gold\":6180}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":179}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1068}},\"db_data_verify\":{\"m\":{\"generate_index\":178}},\"db_role\":{\"m\":{\"star\":2,\"cat_coin\":620,\"gold\":6154}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":-1575354368}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":28}},\"db_data_verify\":{\"m\":{\"generate_index\":177}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206314}},5901:{\"m\":{\"_left_sec\":284462314}}}},\"db_role\":{\"m\":{\"logout_time\":1608964881,\"level\":29,\"lv_tag\":29,\"lv_idx\":29,\"play_time_sec\":1231}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20102:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":21,\"story_progress\":32}},\"db_data_verify\":{\"m\":{\"generate_index\":176}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206322}},5901:{\"m\":{\"_left_sec\":284462322}}}},\"db_role\":{\"m\":{\"main_decorate_stuff\":210}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{210:{\"stuff_id\":210,\"select_id\":1}}}}}}},\"db_data_verify\":{\"m\":{\"generate_index\":175}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206328}},5901:{\"m\":{\"_left_sec\":284462328}}}},\"db_role\":{\"m\":{\"logout_time\":1608964870,\"star\":1,\"play_time_sec\":1220}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1067}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":174}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964853,\"_left_sec\":206344,\"level_point_got\":8,\"_end_ti\":1609171201}},5901:{\"m\":{\"_end_ti\":1893427201,\"_left_sec\":284462344,\"_start_ti\":1608964853}}}},\"db_role\":{\"m\":{\"gold\":6104}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":173}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1066}},\"db_data_verify\":{\"m\":{\"generate_index\":172}},\"db_role\":{\"m\":{\" star\":2,\"cat_coin\":600,\"gold\":6078}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":474.66666666667,\"food_time\":1608964850}},\"db_logger\":{\"m\":{\"seed\":1389625344}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":27}},\"db_data_verify\":{\"m\":{\"saved_index\":170,\"generate_index\":171,\"upload_data_time\":1608964850}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206349}},5901:{\"m\":{\"_left_sec\":284462349}}}},\"db_role\":{\"m\":{\"logout_time\":1608964850,\"level\":28,\"lv_tag\":28,\"lv_idx\":28,\"play_time_sec\":1200}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{1068:[],20092:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":20,\"story_progress\":31}},\"db_tutorial\":{\"m\":{133:true}},\"db_data_verify\":{\"m\":{\"generate_index\":170}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206355}},5901:{\"m\":{\"_left_sec\":284462355}}}},\"db_role\":{\"m\":{\"logout_time\":1608964840,\"main_decorate_stuff\":209,\"play_time_sec\":1190}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{209:{\"stuff_id\":209,\"select_id\":1}}}}}}},\"db_data_verify\":{\"m\":{\"generate_index\":169}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206368}},5901:{\"m\":{\"_left_sec\":284462368}}}},\"db_role\":{\"m\":{\"logout_time\":1608964830,\"star\":1,\"play_time_sec\":1180}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20082:[]}}},\"m\":{\"uid\":1065}},\"db_chapter_map\":{\"s\":{2:{\"m\":{\"award_idx\":2}}}},\"db_talking_choose\":{\"m\":{\"20007-1\":true}},\"db_item_list\":{\"s\":[{\"m\":{\"item_num\":6}}]},\"db_inland_server\":{\"m\":{\"decorate_progress\":19,\"story_progress\":29}},\"db_data_verify\":{\"m\":{\"generate_index\":168}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206371}},5901:{\"m\":{\"_left_sec\":284462371}}}},\"db_role\":{\"m\":{\"energy_infinite\":1608971853,\"cat_coin\":580,\"logout_time\":1608964820,\"eneryg_infinite_left_times\":7024,\"main_decorate_stuff\":208,\"play_time_sec\":1170}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{208:{\"stuff_id\":208,\"select_id\":1}}}}}}},\"db_data_verify\":{\"m\":{\"generate_index\":167}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206382}},5901:{\"m\":{\"_left_sec\":284462382}}}},\"db_role\":{\"m\":{\"logout_time\":1608964810,\"star\":3,\"play_time_sec\":1160}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1064}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":166}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964803,\"_left_sec\":206393,\"level_point_got\":7,\"_end_ti\":1609171200}},5901:{\"m\":{\"_end_ti\":1893427200,\"_left_sec\":284462393,\"_start_ti\":1608964803}}}},\"db_role\":{\"m\":{\"gold\":6028}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":165}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1063}},\"db_data_verify\":{\"m\":{\"generate_index\":164}},\"db_role\":{\"m\":{\"star\":5,\"cat_coin\":480,\"gold\":6003}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":26}},\"db_logger\":{\"m\":{\"seed\":-1132199936}},\"db_tutorial\":{\"m\":{13:true}},\"db_data_verify\":{\"m\":{\"generate_index\":163}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206399}},5901:{\"m\":{\"_left_sec\":284462399}}}},\"db_role\":{\"m\":{\"logout_time\":1608964800,\"level\":27,\"lv_tag\":27,\"lv_idx\":27,\"play_time_sec\":1150}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":475.66666666667,\"food_time\":1608964790}},\"db_data_verify\":{\"m\":{\"generate_index\":162}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206409}},5901:{\"m\":{\"_left_sec\":284462409}}}},\"db_role\":{\"m\":{\"logout_time\":1608964790,\"play_time_sec\":1140}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":161}},\"db_award_map\":{\"d\":[1061]},\"db_item_list\":{\"s\":[{\"m\":{\"item_num\":5}},{\"m\":{\"item_num\":5}}]},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206415}},5901:{\"m\":{\"_left_sec\":284462415}}}},\"db_role\":{\"m\":{\"energy_infinite\":1608970053,\"eneryg_infinite_left_times\":5267,\"logout_time\":1608964780,\"play_time_sec\":1130}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1062}},\"db_award_map\":{\"m\":{1061:{\"item_award_index\":[[2088,[1,2,3]]],\"item_num\":1,\"goods_id\":0,\"reason\":13,\"cost_num\":0,\"uid\":1061,\"opt_info\":[2001,2],\"awards\":[[2024,60],[1006,1],[1007,1]],\"cost_currency\":0}}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":160}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964775,\"_left_sec\":206423,\"level_point_got\":6,\"_end_ti\":1609171201}},5901:{\"m\":{\"_end_ti\":1893427201,\"_left_sec\":284462423,\"_start_ti\":1608964775}}}},\"db_role\":{\"m\":{\"gold\":5953}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":159}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1060}},\"db_data_verify\":{\"m\":{\"generate_index\":158}},\"db_role\":{\"m\":{\"star\":4,\"cat_coin\":460,\"gold\":5927}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":-875036672}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":25}},\"db_data_verify\":{\"m\":{\"generate_index\":157}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206425}},5901:{\"m\":{\"_left_sec\":284462425}}}},\"db_role\":{\"m\":{\"logout_time\":1608964770,\"level\":26,\"lv_tag\":26,\"lv_idx\":26,\"play_time_sec\":1120}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":156}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206440}},5901:{\"m\":{\"_left_sec\":284462440}}}},\"db_role\":{\"m\":{\"logout_time\":1608964760,\"play_time_sec\":1110}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1059}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":155}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964747,\"_left_sec\":206449,\"level_point_got\":5}},5901:{\"m\":{\"_start_ti\":1608964747,\"_left_sec\":284462449}}}},\"db_role\":{\"m\":{\"logout_time\":1608964750,\"play_time_sec\":1100,\"gold\":5877}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":154}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1058}},\"db_data_verify\":{\"m\":{\"generate_index\":153}},\"db_role\":{\"m\":{\"star\":3,\"cat_coin\":440,\"gold\":5856}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":24}},\"db_logger\":{\"m\":{\"seed\":502792192}},\"db_tutorial\":{\"m\":{8:true}},\"db_data_verify\":{\"m\":{\"generate_index\":152}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206455}},5901:{\"m\":{\"_left_sec\":284462455}}}},\"db_role\":{\"m\":{\"logout_time\":1608964741,\"level\":25,\"lv_tag\":25,\"lv_idx\":25,\"play_time_sec\":1091}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":476.66666666667,\"food_time\":1608964730}},\"db_data_verify\":{\"m\":{\"generate_index\":151}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206463}},5901:{\"m\":{\"_left_sec\":284462463}}}},\"db_role\":{\"m\":{\"logout_time\":1608964730,\"play_time_sec\":1080}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1057}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":150}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964725,\"_left_sec\":206472,\"level_point_got\":4}},5901:{\"m\":{\"_start_ti\":1608964725,\"_left_sec\":284462472}}}},\"db_role\":{\"m\":{\"gold\":5806}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":149}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1056}},\"db_data_verify\":{\"m\":{\"generate_index\":148}},\"db_role\":{\"m\":{\"star\":2,\"cat_coin\":420,\"gold\":5777}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":153092096}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":23}},\"db_data_verify\":{\"m\":{\"generate_index\":147}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206477}},5901:{\"m\":{\"_left_sec\":284462477}}}},\"db_role\":{\"m\":{\"logout_time\":1608964720,\"level\":24,\"lv_tag\":24,\"lv_idx\":24,\"play_time_sec\":1070}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20072:[],1067:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":18,\"story_progress\":28}},\"db_tutorial\":{\"m\":{132:true}},\"db_data_verify\":{\"m\":{\"generate_index\":146}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206482}},5901:{\"m\":{\"_left_sec\":284462482}}}},\"db_role\":{\"m\":{\"logout_time\":1608964710,\"main_decorate_stuff\":207,\"play_time_sec\":1060}},\"db_cat_room_map\":{\"s\":{2001:{\"s\":{\"cat_id_list\":{\"m\":{2:4003}}}}}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20062:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":17,\"story_progress\":26}},\"db_talking_choose\":{\"m\":{\"20006-1\":true,\"20005-1\":true}},\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{207:{\"stuff_id\":207,\"select_id\":1}}}}}}},\"db_data_verify\":{\"m\":{\"generate_index\":145}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206498}},5901:{\"m\":{\"_left_sec\":284462498}}}},\"db_role\":{\"m\":{\"logout_time\":1608964700,\"star\":1,\"main_decorate_stuff\":206,\"play_time_sec\":1050}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20052:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":16,\"story_progress\":25}},\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{206:{\"stuff_id\":206,\"select_id\":0}}}}}}},\"db_data_verify\":{\"m\":{\"generate_index\":144}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206510}},5901:{\"m\":{\"_left_sec\":284462510}}}},\"db_role\":{\"m\":{\"logout_time\":1608964690,\"star\":3,\"main_decorate_stuff\":205,\"play_time_sec\":1040}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{205:{\"stuff_id\":205,\"select_id\":1}}}}}}},\"db_data_verify\":{\"m\":{\"generate_index\":143}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206516}},5901:{\"m\":{\"_left_sec\":284462516}}}},\"db_role\":{\"m\":{\"logout_time\":1608964680,\"star\":5,\"play_time_sec\":1030}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":477.66666666667,\"food_time\":1608964670}},\"db_base\":{\"m\":{\"uid\":1055}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":142}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964668,\"_left_sec\":206528,\"level_point_got\":3}},5901:{\"m\":{\"_start_ti\":1608964668,\"_left_sec\":284462528}}}},\"db_role\":{\"m\":{\"logout_time\":1608964670,\"play_time_sec\":1020,\"gold\":5727}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":141}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1054}},\"db_data_verify\":{\"m\":{\"generate_index\":140}},\"db_role\":{\"m\":{\"star\":7,\"cat_coin\":400,\"gold\":5702}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":958398464}},\"db_level\":{\"m\":{\"eng_infinite_tag\":true,\"lv_end_gold\":true,\"flag_lv\":22}},\"db_data_verify\":{\"m\":{\"generate_index\":139}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206533}},5901:{\"m\":{\"_left_sec\":284462533}}}},\"db_role\":{\"m\":{\"logout_time\":1608964660,\"level\":23,\"lv_tag\":23,\"lv_idx\":23,\"play_time_sec\":1010}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":138}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206541}},5901:{\"m\":{\"_left_sec\":284462541}}}}}}">>, - <<"{\"s\":{\"db_award_map\":{\"d\":[1052]},\"db_data_verify\":{\"m\":{\"generate_index\":137}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206547}},5901:{\"m\":{\"_left_sec\":284462547}}}},\"db_role\":{\"m\":{\"energy_infinite\":1608966453,\"eneryg_infinite_left_times\":1800,\"logout_time\":1608964650,\"play_time_sec\":1000}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1053}},\"db_award_map\":{\"m\":{1052:{\"item_award_index\":[[2087,[1]]],\"item_num\":1,\"goods_id\":0,\"reason\":13,\"cost_num\":0,\"uid\":1052,\"opt_info\":[2001,1],\"awards\":[[2024,30]],\"cost_currency\":0}}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":136}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964642,\"_left_sec\":206553,\"level_point_got\":2}},5901:{\"m\":{\"_start_ti\":1608964642,\"_left_sec\":284462553}}}},\"db_role\":{\"m\":{\"gold\":5652}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":135}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1051}},\"db_data_verify\":{\"m\":{\"generate_index\":134}},\"db_role\":{\"m\":{\"star\":6,\"cat_coin\":380,\"gold\":5628}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":21}},\"db_logger\":{\"m\":{\"seed\":954204160}},\"db_tutorial\":{\"m\":{55:true}},\"db_data_verify\":{\"m\":{\"generate_index\":133}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206559}},5901:{\"m\":{\"_left_sec\":284462559}}}},\"db_role\":{\"m\":{\"logout_time\":1608964640,\"level\":22,\"lv_tag\":22,\"lv_idx\":22,\"play_time_sec\":990}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":132}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206568}},5901:{\"m\":{\"_left_sec\":284462568}}}},\"db_role\":{\"m\":{\"logout_time\":1608964630,\"play_time_sec\":980}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1050}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":131}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_start_ti\":1608964616,\"_left_sec\":206578,\"level_point_got\":1}},5901:{\"m\":{\"_start_ti\":1608964616,\"_left_sec\":284462578}}}},\"db_role\":{\"m\":{\"logout_time\":1608964620,\"play_time_sec\":970,\"gold\":5578}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":130}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1049}},\"db_data_verify\":{\"m\":{\"generate_index\":129}},\"db_role\":{\"m\":{\"star\":5,\"cat_coin\":360,\"gold\":5554}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":-1255669760,\"use_doublerocket\":0}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":20}},\"db_data_verify\":{\"m\":{\"generate_index\":128}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_left_sec\":206586}},5901:{\"m\":{\"_left_sec\":284462586}}}},\"db_role\":{\"m\":{\"level\":21,\"lv_tag\":21,\"lv_idx\":21}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":478.66666666667,\"food_time\":1608964610}},\"db_data_verify\":{\"m\":{\"generate_index\":127}},\"db_activity\":{\"s\":{2001:{\"m\":{\"_end_ti\":1609171200,\"_left_sec\":206590}},5901:{\"m\":{\"_left_sec\":284462590}}}},\"db_role\":{\"m\":{\"logout_time\":1608964610,\"play_time_sec\":960}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":126}},\"db_activity\":{\"m\":{5901:{\"show_introduction\":true,\"_left_sec\":284462593,\"sign_day\":1,\"_end_ti\":1893427200,\"_start_ti\":1608964607,\"sign_times\":[],\"can_sign\":true,\"fb_start\":true},2001:{\"fb_start\":true,\"_left_sec\":206607,\"_start_ti\":1608964607,\"level_point_got\":0,\"_end_ti\":1609171214}}},\"db_role\":{\"m\":{\"logout_time\":1608964600,\"play_time_sec\":950}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":125}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1048}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":124}},\"db_role\":{\"m\":{\"gold\":5504}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":123}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1047}},\"db_data_verify\":{\"m\":{\"generate_index\":122}},\"db_role\":{\"m\":{\"star\":4,\"cat_coin\":340,\"gold\":5474}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":19}},\"db_logger\":{\"m\":{\"seed\":-711458816,\"use_doublerocket\":1}},\"db_tutorial\":{\"m\":{6:true}},\"db_data_verify\":{\"m\":{\"generate_index\":121}},\"db_role\":{\"m\":{\"logout_time\":1608964590,\"level\":20,\"lv_tag\":20,\"lv_idx\":20,\"play_time_sec\":940}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":120}},\"db_role\":{\"m\":{\"logout_time\":1608964580,\"play_time_sec\":930}},\"db_tutorial\":{\"m\":{111:true}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1046}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":119}},\"db_role\":{\"m\":{\"gold\":5424}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":118}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1045}},\"db_data_verify\":{\"m\":{\"generate_index\":117}},\"db_role\":{\"m\":{\"star\":3,\"cat_coin\":320,\"gold\":5399}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"use_blackhole\":0,\"seed\":-1422000128}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":18}},\"db_data_verify\":{\"m\":{\"generate_index\":116}},\"db_role\":{\"m\":{\"logout_time\":1608964570,\"level\":19,\"lv_tag\":19,\"lv_idx\":19,\"play_time_sec\":920}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":115}},\"db_role\":{\"m\":{\"logout_time\":1608964560,\"play_time_sec\":910}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1044}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":114}},\"db_role\":{\"m\":{\"gold\":5349}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":113}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1043}},\"db_data_verify\":{\"m\":{\"generate_index\":112}},\"db_role\":{\"m\":{\"star\":2,\"cat_coin\":300,\"gold\":5326}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"remain_food\":479.66666666667,\"food_time\":1608964550}},\"db_logger\":{\"m\":{\"use_blackhole\":1,\"seed\":-1776549888}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":17}},\"db_data_verify\":{\"m\":{\"saved_index\":110,\"generate_index\":111,\"upload_data_time\":1608964550}},\"db_role\":{\"m\":{\"logout_time\":1608964550,\"level\":18,\"lv_tag\":18,\"lv_idx\":18,\"play_time_sec\":900}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":110}},\"db_role\":{\"m\":{\"logout_time\":1608964540,\"play_time_sec\":890}},\"db_tutorial\":{\"m\":{110:true}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"first_add_money_time\":1608964534}},\"db_base\":{\"m\":{\"uid\":1042}},\"db_data_verify\":{\"m\":{\"generate_index\":109}},\"db_role\":{\"m\":{\"gold\":5276}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{1069:[]}}}},\"db_chapter_map\":{\"s\":{2001:{\"s\":{\"stuffs\":{\"m\":{200104:{\"stuff_id\":200104,\"select_id\":1}}}}}}},\"db_cat_stuff_map\":{\"m\":{200104:{\"stuff_id\":200104,\"got_styles\":[1]}}},\"db_inland_server\":{\"m\":{\"story_progress\":24}},\"db_data_verify\":{\"m\":{\"generate_index\":108}},\"db_cat_feed\":{\"m\":{\"remain_food\":480,\"food_time\":1608964530,\"accum_food_time\":480}},\"db_role\":{\"m\":{\"logout_time\":1608964530,\"play_time_sec\":880}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{1066:[]}}}},\"db_inland_server\":{\"m\":{\"story_progress\":23}},\"db_cat_stuff_map\":{\"m\":{200103:{\"stuff_id\":200103,\"got_styles\":[1]}}},\"db_tutorial\":{\"m\":{134:true}},\"db_data_verify\":{\"m\":{\"generate_index\":107}},\"db_chapter_map\":{\"s\":{2001:{\"s\":{\"stuffs\":{\"m\":{200103:{\"stuff_id\":200103,\"select_id\":1}}}}}}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{1065:[]}}}},\"db_inland_server\":{\"m\":{\"story_progress\":22}},\"db_cat_stuff_map\":{\"m\":{200102:{\"stuff_id\":200102,\"got_styles\":[1]}}},\"db_tutorial\":{\"m\":{131:true}},\"db_data_verify\":{\"m\":{\"generate_index\":106}},\"db_chapter_map\":{\"s\":{2001:{\"s\":{\"stuffs\":{\"m\":{200102:{\"stuff_id\":200102,\"select_id\":1}}}}}}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{1064:[]}}}},\"db_inland_server\":{\"m\":{\"story_progress\":21}},\"db_cat_stuff_map\":{\"m\":{200101:{\"stuff_id\":200101,\"got_styles\":[1]}}},\"db_tutorial\":{\"m\":{130:true,129:true}},\"db_data_verify\":{\"m\":{\"generate_index\":105}},\"db_chapter_map\":{\"s\":{2001:{\"s\":{\"stuffs\":{\"m\":{200101:{\"stuff_id\":200101,\"select_id\":1}}}},\"m\":{\"begin\":true}}}},\"db_role\":{\"m\":{\"logout_time\":1608964520,\"play_time_sec\":870}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20042:[]}}},\"m\":{\"uid\":1041}},\"db_chapter_map\":{\"s\":{2:{\"m\":{\"award_idx\":1}}},\"m\":{2001:{\"chapter\":2001,\"begin\":false,\"award_idx\":0,\"stuffs\":[]}}},\"db_talking_choose\":{\"m\":{\"20004-1\":true}},\"db_cat_map\":{\"m\":{4003:{\"name\":\"\">>,\"dress\":false,\"in_star_level\":0,\"dress_id\":0,\"level\":0,\"exp\":0,\"cat_id\":4003,\"have_list\":[]}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":15,\"story_progress\":20}},\"db_data_verify\":{\"m\":{\"generate_index\":104}},\"db_role\":{\"m\":{\"logout_time\":1608964510,\"main_decorate_stuff\":204,\"play_time_sec\":860}},\"db_cat_room_map\":{\"m\":{2001:{\"toy_id_list\":[],\"chapter_id\":2001,\"cat_id_list\":[4001]}}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20032:[]}}}},\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{204:{\"stuff_id\":204,\"select_id\":1}}}}}}},\"db_talking_choose\":{\"m\":{\"20002-1\":true,\"20003-1\":true}},\"db_data_verify\":{\"m\":{\"generate_index\":103}},\"db_inland_server\":{\"m\":{\"decorate_progress\":14,\"story_progress\":19}},\"db_role\":{\"m\":{\"main_decorate_stuff\":203,\"star\":1}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":102}},\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{203:{\"stuff_id\":203,\"select_id\":0}}}}}}},\"db_role\":{\"m\":{\"logout_time\":1608964500,\"star\":2,\"play_time_sec\":850}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1040}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":101}},\"db_role\":{\"m\":{\"logout_time\":1608964490,\"play_time_sec\":840,\"gold\":5256}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":100}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1039}},\"db_data_verify\":{\"m\":{\"generate_index\":99}},\"db_role\":{\"m\":{\"star\":3,\"cat_coin\":280,\"gold\":5230}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":16}},\"db_logger\":{\"m\":{\"seed\":-1736310784,\"use_bombanddril\":0}},\"db_tutorial\":{\"m\":{10:true}},\"db_data_verify\":{\"m\":{\"generate_index\":98}},\"db_role\":{\"m\":{\"logout_time\":1608964482,\"level\":17,\"lv_tag\":17,\"lv_idx\":17,\"play_time_sec\":832}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":97}},\"db_role\":{\"m\":{\"logout_time\":1608964470,\"play_time_sec\":820}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1038}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":96}},\"db_role\":{\"m\":{\"gold\":5180}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":95}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1037}},\"db_data_verify\":{\"m\":{\"generate_index\":94}},\"db_role\":{\"m\":{\"star\":2,\"cat_coin\":260,\"gold\":5152}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":1619525632,\"use_bombanddril\":1}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":15}},\"db_data_verify\":{\"m\":{\"generate_index\":93}},\"db_role\":{\"m\":{\"logout_time\":1608964460,\"level\":16,\"lv_tag\":16,\"lv_idx\":16,\"play_time_sec\":810}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":92}},\"db_role\":{\"m\":{\"logout_time\":1608964450,\"play_time_sec\":800}},\"db_tutorial\":{\"m\":{109:true}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1036}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":91}},\"db_role\":{\"m\":{\"gold\":5102}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":90}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1035}},\"db_data_verify\":{\"m\":{\"generate_index\":89}},\"db_role\":{\"m\":{\"star\":1,\"cat_coin\":240,\"gold\":5076}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":887881728}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":14}},\"db_data_verify\":{\"m\":{\"generate_index\":88}},\"db_role\":{\"m\":{\"logout_time\":1608964440,\"level\":15,\"lv_tag\":15,\"lv_idx\":15,\"play_time_sec\":790}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20022:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":13,\"story_progress\":18}},\"db_data_verify\":{\"m\":{\"generate_index\":87}},\"db_role\":{\"m\":{\"main_decorate_stuff\":202}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":86}},\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{202:{\"stuff_id\":202,\"select_id\":0}}}}}}},\"db_role\":{\"m\":{\"logout_time\":1608964430,\"star\":0,\"play_time_sec\":780}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1034}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":85}},\"db_role\":{\"m\":{\"logout_time\":1608964420,\"play_time_sec\":770,\"gold\":5026}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":84}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1033}},\"db_data_verify\":{\"m\":{\"generate_index\":83}},\"db_role\":{\"m\":{\"star\":1,\"cat_coin\":220,\"gold\":5012}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":13}},\"db_logger\":{\"m\":{\"seed\":364380160}},\"db_tutorial\":{\"m\":{12:true}},\"db_data_verify\":{\"m\":{\"generate_index\":82}},\"db_role\":{\"m\":{\"logout_time\":1608964410,\"level\":14,\"lv_tag\":14,\"lv_idx\":14,\"play_time_sec\":760}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{20012:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":12,\"story_progress\":17}},\"db_talking_choose\":{\"m\":{\"20001-1\":true}},\"db_data_verify\":{\"m\":{\"generate_index\":81}},\"db_role\":{\"m\":{\"logout_time\":1608964390,\"main_decorate_stuff\":201,\"play_time_sec\":740}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"d\":[10032,10081,10082,10052,10022,10112,10072,10042,10102,10012,10092,10062,10000],\"m\":{20001:[]}}}},\"db_chapter_map\":{\"s\":{2:{\"s\":{\"stuffs\":{\"m\":{201:{\"stuff_id\":201,\"select_id\":0}}}},\"m\":{\"begin\":true}}}},\"db_talking_choose\":{\"m\":{\"R1-12\":true}},\"db_data_verify\":{\"m\":{\"generate_index\":80}},\"db_inland_server\":{\"m\":{\"story_progress\":16}},\"db_role\":{\"m\":{\"logout_time\":1608964380,\"star\":0,\"play_time_sec\":730}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10112:[]}}},\"m\":{\"uid\":1032}},\"db_inland_server\":{\"m\":{\"decorate_progress\":11,\"story_progress\":14}},\"db_chapter_map\":{\"s\":[{\"m\":{\"award_idx\":2}}]},\"db_data_verify\":{\"m\":{\"generate_index\":79}},\"db_role\":{\"m\":{\"cat_coin\":200,\"main_decorate_stuff\":111,\"gold\":4962}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10102:[]}}}},\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{111:{\"stuff_id\":111,\"select_id\":1}}}}}],\"m\":{2:{\"chapter\":2,\"begin\":false,\"award_idx\":0,\"stuffs\":[]}}},\"db_talking_choose\":{\"m\":{\"R1-11\":true}},\"db_data_verify\":{\"m\":{\"generate_index\":78}},\"db_inland_server\":{\"m\":{\"decorate_progress\":10,\"story_progress\":13}},\"db_role\":{\"m\":{\"chapter\":2,\"star\":1,\"logout_time\":1608964370,\"main_decorate_stuff\":110,\"play_time_sec\":720}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10092:[]}}}},\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{110:{\"stuff_id\":110,\"select_id\":1}}}}}]},\"db_talking_choose\":{\"m\":{\"R1-9A\":true,\"R1-10A\":true}},\"db_data_verify\":{\"m\":{\"generate_index\":77}},\"db_inland_server\":{\"m\":{\"decorate_progress\":9,\"story_progress\":12}},\"db_role\":{\"m\":{\"logout_time\":1608964360,\"star\":2,\"main_decorate_stuff\":109,\"play_time_sec\":710}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10082:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":8,\"story_progress\":11}},\"db_talking_choose\":{\"m\":{\"R1-8A\":true}},\"db_tutorial\":{\"m\":{108:true}},\"db_data_verify\":{\"m\":{\"generate_index\":76}},\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{109:{\"stuff_id\":109,\"select_id\":1}}}}}]},\"db_role\":{\"m\":{\"logout_time\":1608964350,\"star\":3,\"main_decorate_stuff\":108,\"play_time_sec\":700}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10072:[],10081:[]}}}},\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{108:{\"stuff_id\":108,\"select_id\":1}}}}}]},\"db_talking_choose\":{\"m\":{\"R1-7A\":true}},\"db_data_verify\":{\"m\":{\"generate_index\":75}},\"db_inland_server\":{\"m\":{\"decorate_progress\":7,\"story_progress\":10}},\"db_role\":{\"m\":{\"logout_time\":1608964340,\"star\":4,\"main_decorate_stuff\":107,\"play_time_sec\":690}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10062:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":6,\"story_progress\":8}},\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{107:{\"stuff_id\":107,\"select_id\":1}}}}}]},\"db_data_verify\":{\"m\":{\"generate_index\":74}},\"db_role\":{\"m\":{\"logout_time\":1608964330,\"star\":5,\"main_decorate_stuff\":106,\"play_time_sec\":680}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":73}},\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{106:{\"stuff_id\":106,\"select_id\":1}}}}}]},\"db_role\":{\"m\":{\"logout_time\":1608964320,\"star\":6,\"play_time_sec\":670}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1031}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":72}},\"db_role\":{\"m\":{\"logout_time\":1608964310,\"play_time_sec\":660,\"gold\":1962}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":71}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1030}},\"db_data_verify\":{\"m\":{\"generate_index\":70}},\"db_role\":{\"m\":{\"star\":7,\"cat_coin\":100,\"gold\":1934}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":-1906311168}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":12}},\"db_data_verify\":{\"m\":{\"generate_index\":69}},\"db_role\":{\"m\":{\"logout_time\":1608964300,\"level\":13,\"lv_tag\":13,\"lv_idx\":13,\"play_time_sec\":650}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":68}},\"db_role\":{\"m\":{\"logout_time\":1608964290,\"play_time_sec\":640}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1029}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":67}},\"db_role\":{\"m\":{\"logout_time\":1608964280,\"play_time_sec\":630,\"gold\":1884}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":66}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1028}},\"db_data_verify\":{\"m\":{\"generate_index\":65}},\"db_role\":{\"m\":{\"star\":6,\"cat_coin\":80,\"gold\":1860}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":11}},\"db_logger\":{\"m\":{\"seed\":-1530396672}},\"db_tutorial\":{\"m\":{11:true}},\"db_data_verify\":{\"m\":{\"generate_index\":64}},\"db_role\":{\"m\":{\"logout_time\":1608964270,\"level\":12,\"lv_tag\":12,\"lv_idx\":12,\"play_time_sec\":620}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":63}},\"db_role\":{\"m\":{\"logout_time\":1608964260,\"play_time_sec\":610}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1027}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"saved_index\":61,\"generate_index\":62,\"upload_data_time\":1608964250}},\"db_role\":{\"m\":{\"logout_time\":1608964250,\"play_time_sec\":600,\"gold\":1810}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":61}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1026}},\"db_data_verify\":{\"m\":{\"generate_index\":60}},\"db_role\":{\"m\":{\"star\":5,\"cat_coin\":60,\"gold\":1782}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":10}},\"db_logger\":{\"m\":{\"seed\":1781137408}},\"db_tutorial\":{\"m\":{27:true}},\"db_data_verify\":{\"m\":{\"generate_index\":59}},\"db_role\":{\"m\":{\"logout_time\":1608964241,\"level\":11,\"lv_tag\":11,\"lv_idx\":11,\"play_time_sec\":591}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":58}},\"db_role\":{\"m\":{\"logout_time\":1608964230,\"play_time_sec\":580}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1025}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":57}},\"db_role\":{\"m\":{\"gold\":1732}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":56}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1024}},\"db_data_verify\":{\"m\":{\"generate_index\":55}},\"db_role\":{\"m\":{\"star\":4,\"cat_coin\":40,\"gold\":1704}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":-166592512}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":9}},\"db_data_verify\":{\"m\":{\"generate_index\":54}},\"db_role\":{\"m\":{\"logout_time\":1608964220,\"level\":10,\"lv_tag\":10,\"lv_idx\":10,\"play_time_sec\":570}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":53}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1023}},\"db_data_verify\":{\"m\":{\"generate_index\":52}},\"db_activity\":{\"m\":{1001:[]}},\"db_role\":{\"m\":{\"logout_time\":1608964210,\"play_time_sec\":560,\"gold\":1654}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1022}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_tutorial\":{\"m\":{57:true}},\"db_data_verify\":{\"m\":{\"generate_index\":51}},\"db_role\":{\"m\":{\"gold\":1554}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":50}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1021}},\"db_data_verify\":{\"m\":{\"generate_index\":49}},\"db_role\":{\"m\":{\"star\":3,\"cat_coin\":20,\"gold\":1526}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":1123680256}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":8}},\"db_data_verify\":{\"m\":{\"generate_index\":48}},\"db_role\":{\"m\":{\"logout_time\":1608964192,\"level\":9,\"lv_tag\":9,\"lv_idx\":9,\"play_time_sec\":542}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":47}},\"db_role\":{\"m\":{\"logout_time\":1608964180,\"play_time_sec\":530}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1020}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_tutorial\":{\"m\":{7:true}},\"db_data_verify\":{\"m\":{\"generate_index\":46}},\"db_role\":{\"m\":{\"gold\":1476}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":45}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1019}},\"db_data_verify\":{\"m\":{\"generate_index\":44}},\"db_role\":{\"m\":{\"star\":2,\"gold\":1448}}}}">>, - <<"{\"s\":{\"db_logger\":{\"m\":{\"seed\":-1272840192}},\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":7}},\"db_data_verify\":{\"m\":{\"generate_index\":43}},\"db_role\":{\"m\":{\"level\":8,\"lv_tag\":8,\"lv_idx\":8}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":42}},\"db_role\":{\"m\":{\"logout_time\":1608964170,\"play_time_sec\":520}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1018}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":41}},\"db_role\":{\"m\":{\"logout_time\":1608964160,\"play_time_sec\":510,\"gold\":1398}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":40}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1017}},\"db_data_verify\":{\"m\":{\"generate_index\":39}},\"db_role\":{\"m\":{\"star\":1,\"gold\":1370}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":6}},\"db_logger\":{\"m\":{\"seed\":919470080}},\"db_tutorial\":{\"m\":{56:true}},\"db_data_verify\":{\"m\":{\"generate_index\":38}},\"db_role\":{\"m\":{\"level\":7,\"lv_tag\":7,\"lv_idx\":7}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10052:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":5,\"story_progress\":7}},\"db_talking_choose\":{\"m\":{\"R1-6\":true}},\"db_data_verify\":{\"m\":{\"generate_index\":37}},\"db_role\":{\"m\":{\"logout_time\":1608964150,\"main_decorate_stuff\":105,\"play_time_sec\":500}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":36}},\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{105:{\"stuff_id\":105,\"select_id\":1}}}}}]},\"db_role\":{\"m\":{\"logout_time\":1608964140,\"star\":0,\"play_time_sec\":490}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10042:[]}}},\"m\":{\"uid\":1016}},\"db_chapter_map\":{\"s\":[{\"m\":{\"award_idx\":1}}]},\"db_talking_choose\":{\"m\":{\"R1-4\":true,\"R1-5A\":true}},\"db_data_verify\":{\"m\":{\"generate_index\":35}},\"db_cat_map\":{\"m\":{4002:{\"name\":\"\">>,\"dress\":false,\"in_star_level\":0,\"dress_id\":0,\"level\":0,\"exp\":0,\"cat_id\":4002,\"have_list\":[]}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":4,\"story_progress\":6}},\"db_role\":{\"m\":{\"logout_time\":1608964130,\"main_decorate_stuff\":104,\"play_time_sec\":480}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{104:{\"stuff_id\":104,\"select_id\":1}}}}}]},\"db_tutorial\":{\"m\":{107:true}},\"db_data_verify\":{\"m\":{\"generate_index\":34}},\"db_role\":{\"m\":{\"logout_time\":1608964120,\"star\":1,\"play_time_sec\":470}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1015}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":33}},\"db_role\":{\"m\":{\"logout_time\":1608964110,\"play_time_sec\":460,\"gold\":1320}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":32}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1014}},\"db_data_verify\":{\"m\":{\"generate_index\":31}},\"db_role\":{\"m\":{\"star\":2,\"gold\":1300}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":5}},\"db_logger\":{\"m\":{\"seed\":1919942656}},\"db_tutorial\":{\"m\":{5:true}},\"db_data_verify\":{\"m\":{\"generate_index\":30}},\"db_role\":{\"m\":{\"logout_time\":1608964100,\"level\":6,\"lv_tag\":6,\"lv_idx\":6,\"play_time_sec\":450}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":29}},\"db_role\":{\"m\":{\"logout_time\":1608964070,\"play_time_sec\":420}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1013}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":28}},\"db_role\":{\"m\":{\"logout_time\":1608964060,\"play_time_sec\":410,\"gold\":1250}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":27}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1012}},\"db_data_verify\":{\"m\":{\"generate_index\":26}},\"db_role\":{\"m\":{\"star\":1,\"gold\":1232}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":4}},\"db_logger\":{\"m\":{\"seed\":-889716736}},\"db_tutorial\":{\"m\":{4:true}},\"db_data_verify\":{\"m\":{\"generate_index\":25}},\"db_role\":{\"m\":{\"logout_time\":1608964050,\"level\":5,\"lv_tag\":5,\"lv_idx\":5,\"play_time_sec\":400}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10032:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":3,\"story_progress\":5}},\"db_talking_choose\":{\"m\":{\"R1-3A\":true}},\"db_tutorial\":{\"m\":{106:true}},\"db_data_verify\":{\"m\":{\"generate_index\":24}},\"db_role\":{\"m\":{\"logout_time\":1608964000,\"main_decorate_stuff\":103,\"play_time_sec\":350}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{103:{\"stuff_id\":103,\"select_id\":0}}}}}]},\"db_tutorial\":{\"m\":{105:true}},\"db_data_verify\":{\"m\":{\"generate_index\":23}},\"db_role\":{\"m\":{\"logout_time\":1608963990,\"star\":0,\"play_time_sec\":340}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1011}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":22}},\"db_role\":{\"m\":{\"logout_time\":1608963980,\"play_time_sec\":330,\"gold\":1182}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":21}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1010}},\"db_data_verify\":{\"m\":{\"generate_index\":20}},\"db_role\":{\"m\":{\"star\":1,\"gold\":1168}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"flag_lv\":3}},\"db_logger\":{\"m\":{\"seed\":-411566080}},\"db_tutorial\":{\"m\":{3:true}},\"db_data_verify\":{\"m\":{\"saved_index\":18,\"generate_index\":19,\"upload_data_time\":1608963950}},\"db_role\":{\"m\":{\"logout_time\":1608963970,\"level\":4,\"lv_tag\":4,\"lv_idx\":4,\"play_time_sec\":320}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10022:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":2,\"story_progress\":4}},\"db_talking_choose\":{\"m\":{\"R1-2A\":true}},\"db_tutorial\":{\"m\":{104:true}},\"db_data_verify\":{\"m\":{\"generate_index\":18}},\"db_role\":{\"m\":{\"logout_time\":1608963920,\"main_decorate_stuff\":102,\"play_time_sec\":270}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{102:{\"stuff_id\":102,\"select_id\":0}}}}}]},\"db_tutorial\":{\"m\":{103:true}},\"db_data_verify\":{\"m\":{\"generate_index\":17}},\"db_role\":{\"m\":{\"logout_time\":1608963910,\"star\":0,\"play_time_sec\":260}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1009}},\"db_level\":{\"m\":{\"lv_end_gold\":false}},\"db_data_verify\":{\"m\":{\"generate_index\":16}},\"db_role\":{\"m\":{\"logout_time\":1608963870,\"play_time_sec\":220,\"gold\":1118}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":15}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1008}},\"db_data_verify\":{\"m\":{\"generate_index\":14}},\"db_role\":{\"m\":{\"star\":1,\"gold\":1100}}}}">>, - <<"{\"s\":{\"db_level\":{\"m\":{\"flag_lv\":2}},\"db_logger\":{\"m\":{\"seed\":-392560640}},\"db_tutorial\":{\"m\":{2:true}},\"db_data_verify\":{\"m\":{\"generate_index\":13}},\"db_role\":{\"m\":{\"logout_time\":1608963860,\"level\":3,\"lv_tag\":3,\"lv_idx\":3,\"play_time_sec\":210}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10012:[]}}}},\"db_inland_server\":{\"m\":{\"decorate_progress\":1,\"story_progress\":3}},\"db_talking_choose\":{\"m\":{\"R1-1A\":true}},\"db_tutorial\":{\"m\":{102:true}},\"db_data_verify\":{\"m\":{\"generate_index\":12}},\"db_role\":{\"m\":{\"logout_time\":1608963780,\"main_decorate_stuff\":101,\"play_time_sec\":130}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{1001:[]}}}},\"db_inland_server\":{\"m\":{\"story_progress\":2}},\"db_tutorial\":{\"m\":{101:true}},\"db_data_verify\":{\"m\":{\"generate_index\":11}},\"db_chapter_map\":{\"s\":[{\"s\":{\"stuffs\":{\"m\":{101:{\"stuff_id\":101,\"select_id\":0}}}}}]},\"db_role\":{\"m\":{\"logout_time\":1608963770,\"star\":0,\"play_time_sec\":120}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":10}}}}">>, - <<"{\"s\":{\"db_base\":{\"m\":{\"uid\":1007}},\"db_data_verify\":{\"m\":{\"generate_index\":9}},\"db_role\":{\"m\":{\"star\":1,\"gold\":1050}}}}">>, - <<"{\"s\":{\"db_cat_gift_new\":{\"s\":{\"cur_gift\":{\"m\":{\"gift_id\":0,\"cat_id\":0,\"end_time\":0}}}},\"db_logger\":{\"m\":{\"seed\":775028736}},\"db_tutorial\":{\"m\":[true]},\"db_data_verify\":{\"m\":{\"generate_index\":8}},\"db_level\":{\"s\":{\"lv_failed_count\":{\"m\":{\"Empty\":[0,0,0]}}},\"m\":{\"eng_infinite_tag\":false,\"lv_end_gold\":true,\"flag_lv\":1}},\"db_role\":{\"m\":{\"logout_time\":1608963750,\"level\":2,\"lv_tag\":2,\"lv_idx\":2,\"play_time_sec\":100}}}}">>, - <<"{\"s\":{\"db_base\":{\"s\":{\"talking_data\":{\"m\":{10000:[]}}}},\"db_inland_server\":{\"m\":{\"story_progress\":1}},\"db_data_verify\":{\"m\":{\"saved_index\":1,\"generate_index\":7,\"upload_data_time\":1608963651}},\"db_role\":{\"m\":{\"role_name\":\"Tyler\"}},\"db_monthcard\":{\"m\":{\"week_day\":6,\"left_os_time\":1608963651,\"day_left_time\":34749,\"day_left_os_time\":1608998400}}}}">>, - <<"{\"s\":{\"db_cat_feed\":{\"m\":{\"shovel\":10}},\"db_data_verify\":{\"m\":{\"server_data_version_code\":1,\"generate_index\":6}},\"db_store_ad\":{\"m\":{\"today_cookie\":18622}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":5}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":4}},\"db_role\":{\"m\":{\"server_day_idx\":18622}},\"db_cat_gift_new\":{\"s\":{\"cur_gift\":{\"m\":{\"ad_watch\":0}}}}}}">>, - <<"{\"s\":{\"db_chapter_map\":{\"s\":[{\"m\":{\"begin\":true}}]},\"db_level\":{\"m\":{\"activity_form\":\"Empty\"}},\"db_data_verify\":{\"m\":{\"generate_index\":3}},\"db_cat_ad\":{\"m\":{\"ad_num\":1,\"is_unlock\":true}}}}">>, - <<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":2}},\"db_logger\":{\"m\":{\"day_num\":1,\"day_idx\":18622}},\"db_cat_map\":{\"m\":{4001:{\"name\":\"\">>,\"dress\":false,\"in_star_level\":0,\"dress_id\":0,\"level\":0,\"exp\":0,\"cat_id\":4001,\"have_list\":[]}}},\"db_role\":{\"m\":{\"login_day_count\":1,\"last_login_day_index\":\"361\"}}}}">> - ], - client_map => #{}, cross_hammer => 4, d_rockets => 5, energy => 0, - event_idx => 212, exchange => 4, game_time => 0, gold => 6416, hammer => 5, - house_data => - "{\"db_stuff_timeline\":[],\"db_chapter_map\":[[\"chapter\",\"begin\",\"award_idx\",\"stuffs\"],[2001,2001,true,0,[3,3,3,3]]],\"db_stuff_child_visible\":[],\"version\":\"0.2.0\",\"db_item_list\":[],\"db_timeline_stuff_style\":[],\"db_cat_map\":[[\"name\",\"dress\",\"in_star_level\",\"cat_id\",\"level\",\"exp\",\"dress_id\",\"have_list\"],{1:4003,2:\"\",3:false,4:0,5:4003,6:0,7:0,8:0,9:[]},{1:4001,2:\"\",3:false,4:0,5:4001,6:0,7:0,8:0,9:[]}],\"db_cat_room_map\":[[\"toy_id_list\",\"chapter_id\",\"cat_id_list\"],[2001,[],2001,[4001,4003]]]}", - key_data => - #{'$map_tab' => key_data, - activity => - #{'$map_tab' => act_dv, - act_comic_strips => #{5101 => {0, 2}}, - act_icecream_truck => - #{4001 => - {0, - #{-1047817473 => 0, -884040730 => 0, 132422520 => 40}, - #{1 => 0}}}, - act_milk => #{2001 => {3, 1}}, - act_sign => 18622}, - ad => - #{21 => 0, 25 => 0, 53 => 0, 81 => 0, 83 => 0, 84 => 0, 86 => 0, 87 => 0, - 104 => 0, '$map_tab' => ad_dv, day => 18622}, - award_uid => 0, - cat_room => {2001, [200104, 200103, 200102, 200101]}, - cats => <<"p">>, - chapter => {202, 2, "ÔÓÒÑÐÏÎÍÌËÊÉ"}, - feed => {0, 0, 0, 0, 1}, - guide_awards => [[]], - icons => <<>>, - limit_room => {[], []}, - milk_bottle => 1, ml => [], special_back => #{}, spoint => 0, - store => <<>>, styles => [] - }, - lv => 32, res_version => "0.2.0", role_id => 10010001, story => 1, - sync_idx => 1, token => 0, upload_time => 1609147409, verify_result => 0, - version => <<"0.0.0">>}. - -get_test2() -> - #{'$map_tab' => tb_client, - black_hole => 4, - bomb_drill => 4, - cat_coin => 0, - client_data => <<>>, - %%<<123,34,100,98,95,97,119,97,114,100,95,109,97,112,34,58,91,93,44,34,100,98,95,99,97,116,95,103,105,102,116,34,58,123,34,109,97,120,95,97,119,97,114,100,95,110,117,109,34,58,102,97,108,115,101,44,34,97,119,97,114,100,95,110,117,109,34,58,102,97,108,115,101,44,34,115,116,97,114,116,95,116,105,34,58,48,44,34,108,101,102,116,95,115,101,99,34,58,48,44,34,97,119,97,114,100,115,34,58,102,97,108,115,101,44,34,101,110,100,95,116,105,34,58,48,44,34,102,105,114,115,116,95,102,101,101,100,95,103,105,102,116,34,58,102,97,108,115,101,44,34,115,101,108,101,99,116,95,104,111,117,114,95,116,121,112,101,34,58,102,97,108,115,101,125,44,34,100,98,95,99,97,116,95,97,100,34,58,123,34,105,115,95,117,110,108,111,99,107,34,58,116,114,117,101,44,34,97,100,95,110,117,109,34,58,49,44,34,99,100,95,116,105,109,101,34,58,48,125,44,34,100,98,95,99,108,111,116,104,101,115,95,111,117,116,95,99,97,116,34,58,91,93,44,34,100,98,95,99,97,116,95,101,118,101,110,116,34,58,123,34,110,117,109,34,58,48,44,34,105,115,95,117,110,108,111,99,107,34,58,102,97,108,115,101,44,34,105,115,95,105,110,105,116,34,58,102,97,108,115,101,44,34,115,116,97,114,116,95,116,105,109,101,34,58,48,125,44,34,100,98,95,102,105,110,105,115,104,101,100,95,97,99,116,105,118,105,116,121,34,58,91,93,44,34,100,98,95,105,110,108,97,110,100,95,115,101,114,118,101,114,34,58,123,34,117,117,105,100,34,58,34,34,44,34,100,101,99,111,114,97,116,101,95,112,114,111,103,114,101,115,115,34,58,51,44,34,97,99,99,111,117,110,116,95,99,104,97,110,110,101,108,34,58,34,34,44,34,99,104,97,110,110,101,108,95,114,111,108,101,34,58,91,93,44,34,98,105,110,100,95,97,99,99,111,117,110,116,95,97,119,97,114,100,34,58,102,97,108,115,101,44,34,115,116,111,114,121,95,112,114,111,103,114,101,115,115,34,58,53,44,34,108,97,115,116,95,97,99,99,111,117,110,116,95,99,104,97,110,110,101,108,34,58,34,34,125,44,34,100,98,95,99,104,97,112,116,101,114,95,109,97,112,34,58,91,91,34,115,116,117,102,102,115,34,44,34,97,119,97,114,100,95,105,100,120,34,44,34,98,101,103,105,110,34,44,34,99,104,97,112,116,101,114,34,93,44,91,49,44,91,49,44,49,44,49,93,44,48,44,116,114,117,101,44,49,93,93,44,34,100,98,95,116,105,109,101,108,105,110,101,95,115,116,117,102,102,95,115,116,121,108,101,34,58,91,93,44,34,100,98,95,99,97,116,95,103,105,102,116,95,110,101,119,34,58,123,34,103,105,102,116,115,34,58,91,93,44,34,105,115,95,117,110,108,111,99,107,34,58,102,97,108,115,101,44,34,99,117,114,95,103,105,102,116,34,58,123,34,97,100,95,119,97,116,99,104,34,58,48,44,34,103,105,102,116,95,105,100,34,58,48,44,34,99,97,116,95,105,100,34,58,48,44,34,101,110,100,95,116,105,109,101,34,58,48,125,125,44,34,100,98,95,112,114,111,99,101,115,115,34,58,123,34,112,114,111,99,101,115,115,34,58,91,93,125,44,34,100,98,95,105,110,108,97,110,100,95,105,100,105,112,34,58,123,34,99,111,105,110,95,98,101,102,111,114,101,95,99,111,117,110,116,34,58,48,44,34,99,111,105,110,95,111,112,116,95,105,100,34,58,48,44,34,99,111,105,110,95,97,102,116,101,114,95,99,111,117,110,116,34,58,48,44,34,98,97,110,100,95,106,111,105,110,95,114,97,110,107,105,110,103,95,105,110,102,111,34,58,91,93,44,34,99,111,105,110,95,102,114,101,101,122,101,95,105,110,102,111,34,58,48,125,44,34,100,98,95,108,101,118,101,108,34,58,123,34,102,108,97,103,95,108,118,34,58,53,44,34,108,118,95,101,110,100,95,103,111,108,100,34,58,102,97,108,115,101,44,34,108,101,118,101,108,95,98,117,121,95,116,105,109,101,34,58,48,44,34,101,110,103,95,105,110,102,105,110,105,116,101,95,116,97,103,34,58,102,97,108,115,101,44,34,97,99,116,105,118,105,116,121,95,102,111,114,109,34,58,34,69,109,112,116,121,34,44,34,108,118,95,102,97,105,108,110,95,97,100,118,105,99,101,34,58,48,44,34,108,101,118,101,108,95,105,116,101,109,95,108,105,115,116,34,58,91,93,44,34,117,115,101,95,105,116,101,109,95,108,105,115,116,34,58,91,93,44,34,99,111,110,116,105,110,117,101,95,99,111,115,116,34,58,48,44,34,108,118,95,116,121,112,101,34,58,48,44,34,115,101,116,95,114,101,115,117,108,116,34,58,48,44,34,108,101,118,101,108,95,115,116,97,116,101,34,58,48,44,34,108,118,95,102,97,105,108,101,100,95,99,111,117,110,116,34,58,123,34,69,109,112,116,121,34,58,91,48,44,48,44,48,93,125,125,44,34,100,98,95,116,117,116,111,114,105,97,108,34,58,123,48,58,51,49,44,51,58,49,48,48,56,125,44,34,100,98,95,99,97,116,95,115,116,117,102,102,95,109,97,112,34,58,91,93,44,34,100,98,95,100,97,116,97,95,118,101,114,105,102,121,34,58,123,34,115,101,114,118,101,114,95,100,97,116,97,95,118,101,114,115,105,111,110,95,99,111,100,101,34,58,49,44,34,115,97,118,101,100,95,105,110,100,101,120,34,58,49,44,34,117,112,108,111,97,100,95,100,97,116,97,95,116,105,109,101,34,58,49,54,48,57,57,57,48,52,56,55,44,34,103,101,110,101,114,97,116,101,95,105,110,100,101,120,34,58,51,56,125,44,34,100,98,95,97,99,116,105,118,105,116,121,95,115,101,116,116,108,101,34,58,123,34,105,110,102,105,110,105,116,101,95,99,104,97,108,108,101,110,103,101,95,105,110,102,111,34,58,91,48,44,48,44,48,93,125,44,34,100,98,95,98,97,115,101,34,58,123,34,116,97,108,107,105,110,103,95,100,97,116,97,34,58,123,49,48,48,51,50,58,91,93,44,49,48,48,49,58,91,93,44,49,48,48,49,50,58,91,93,44,49,48,48,50,50,58,91,93,44,49,48,48,48,48,58,91,93,125,44,34,117,105,100,34,58,49,48,49,53,44,34,116,97,108,107,105,110,103,95,115,99,101,110,101,95,105,110,102,111,34,58,91,93,125,44,34,100,98,95,109,111,110,116,104,99,97,114,100,34,58,123,34,99,97,114,100,95,100,97,121,34,58,48,44,34,99,97,114,100,95,105,100,34,58,48,44,34,116,97,103,34,58,48,44,34,100,97,121,95,108,101,102,116,95,116,105,109,101,34,58,52,52,55,49,51,44,34,100,97,121,95,108,101,102,116,95,111,115,95,116,105,109,101,34,58,49,54,49,48,48,51,53,50,48,48,44,34,108,101,102,116,95,111,115,95,116,105,109,101,34,58,49,54,48,57,57,57,48,52,56,55,44,34,108,101,102,116,95,115,101,99,34,58,48,44,34,115,104,111,119,95,109,111,110,116,104,99,97,114,100,95,99,101,108,108,34,58,91,93,44,34,104,97,118,101,95,103,111,116,34,58,102,97,108,115,101,44,34,119,101,101,107,95,100,97,121,34,58,52,44,34,100,105,115,99,95,110,117,109,34,58,48,125,44,34,100,98,95,116,97,108,107,105,110,103,95,99,104,111,111,115,101,34,58,123,34,82,49,45,49,65,34,58,116,114,117,101,44,34,82,49,45,51,65,34,58,116,114,117,101,44,34,82,49,45,50,65,34,58,116,114,117,101,125,44,34,100,98,95,99,97,116,95,115,116,111,114,121,34,58,91,93,44,34,100,98,95,99,97,116,95,102,101,101,100,34,58,123,34,114,101,109,97,105,110,95,102,111,111,100,34,58,48,44,34,109,111,110,101,121,95,116,105,109,101,34,58,48,44,34,114,101,109,97,105,110,95,119,97,116,101,114,34,58,48,44,34,102,111,111,100,95,116,105,109,101,34,58,48,44,34,102,105,114,115,116,95,97,100,100,95,109,111,110,101,121,95,116,105,109,101,34,58,48,44,34,97,99,99,117,109,95,102,111,111,100,95,116,105,109,101,34,58,48,44,34,99,108,101,97,110,95,102,114,100,95,116,105,109,101,34,58,91,93,44,34,109,111,110,101,121,95,108,105,115,116,34,58,91,93,44,34,97,99,99,117,109,95,119,97,116,101,114,95,116,105,109,101,34,58,48,44,34,97,99,99,117,109,95,109,111,110,101,121,34,58,48,44,34,117,115,101,100,95,115,104,111,118,101,108,34,58,48,44,34,119,97,116,101,114,95,116,105,109,101,34,58,48,44,34,108,97,115,116,95,97,100,100,95,100,97,121,34,58,48,44,34,114,101,109,97,105,110,95,109,111,110,101,121,34,58,48,44,34,100,97,121,95,105,110,100,101,120,34,58,48,44,34,115,104,111,118,101,108,34,58,49,48,125,44,34,100,98,95,97,99,116,105,118,105,116,121,34,58,91,93,44,34,100,98,95,99,97,116,95,101,120,116,114,97,34,58,123,34,117,110,108,111,99,107,95,108,105,115,116,34,58,91,93,125,44,34,100,98,95,104,101,97,100,34,58,123,34,115,104,111,119,95,104,101,97,100,34,58,34,49,34,44,34,111,119,110,95,104,101,97,100,34,58,91,93,125,44,34,100,98,95,99,97,116,95,114,111,111,109,95,109,97,112,34,58,91,93,44,34,100,98,95,115,116,117,102,102,95,116,105,109,101,108,105,110,101,34,58,91,93,44,34,100,98,95,99,97,116,95,114,101,108,97,116,105,111,110,34,58,123,52,48,48,50,58,91,51,93,44,52,48,48,49,58,91,49,93,125,44,34,100,98,95,108,111,103,103,101,114,34,58,123,34,100,97,121,95,105,100,120,34,58,49,56,54,51,52,44,34,103,111,108,100,95,99,111,115,116,34,58,48,44,34,100,97,121,95,110,117,109,34,58,49,44,34,111,110,108,105,110,101,95,115,101,99,34,58,53,57,53,48,44,34,117,115,101,95,100,111,117,98,108,101,114,111,99,107,101,116,34,58,48,44,34,115,101,101,100,34,58,49,57,49,57,57,52,50,54,53,54,44,34,99,111,105,110,95,99,111,115,116,34,58,48,44,34,108,111,103,34,58,48,44,34,117,115,101,95,104,97,109,109,101,114,34,58,48,44,34,117,115,101,95,103,108,111,118,101,34,58,48,44,34,114,101,115,104,117,102,102,108,101,34,58,48,44,34,117,115,101,95,99,114,111,115,115,104,97,109,109,101,114,34,58,48,44,34,117,115,101,95,98,108,97,99,107,104,111,108,101,34,58,48,44,34,117,115,101,95,98,111,109,98,97,110,100,100,114,105,108,34,58,48,44,34,112,97,121,95,110,117,109,34,58,48,125,44,34,100,98,95,109,97,105,108,95,108,105,115,116,34,58,91,93,44,34,100,98,95,99,97,116,95,109,97,112,34,58,91,91,34,108,101,118,101,108,34,44,34,104,97,118,101,95,108,105,115,116,34,44,34,99,97,116,95,105,100,34,44,34,101,120,112,34,44,34,100,114,101,115,115,34,44,34,105,110,95,115,116,97,114,95,108,101,118,101,108,34,44,34,110,97,109,101,34,44,34,100,114,101,115,115,95,105,100,34,93,44,123,49,58,52,48,48,49,44,50,58,48,44,51,58,91,93,44,52,58,52,48,48,49,44,53,58,48,44,54,58,102,97,108,115,101,44,55,58,48,44,56,58,34,34,44,57,58,48,125,93,44,34,100,98,95,115,116,111,114,101,95,97,100,34,58,123,34,116,111,100,97,121,95,99,111,111,107,105,101,34,58,49,56,54,51,52,44,34,119,97,116,99,104,101,100,95,99,111,117,110,116,34,58,48,44,34,110,101,120,116,95,97,100,95,116,105,109,101,34,58,48,125,44,34,100,98,95,114,111,108,101,34,58,123,34,108,101,118,101,108,34,58,54,44,34,114,111,108,101,95,110,97,109,101,34,58,34,230,183,177,232,164,144,231,154,132,231,133,142,232,155,139,34,44,34,115,112,101,99,105,97,108,95,112,111,105,110,116,34,58,48,44,34,112,108,97,121,95,116,105,109,101,95,115,101,99,34,58,53,57,53,48,44,34,101,110,101,114,103,121,95,116,105,109,101,34,58,48,44,34,108,111,103,105,110,95,116,105,109,101,34,58,49,54,48,57,57,57,53,54,53,54,44,34,98,104,95,105,110,102,105,110,105,116,101,95,108,101,102,116,95,115,101,99,34,58,48,44,34,108,111,103,111,117,116,95,116,105,109,101,34,58,49,54,48,57,57,57,53,54,53,54,44,34,104,97,118,101,95,115,105,103,110,34,58,91,93,44,34,108,118,95,116,97,103,34,58,54,44,34,98,100,95,105,110,102,105,110,105,116,101,95,108,101,102,116,95,115,101,99,34,58,48,44,34,99,104,97,112,116,101,114,34,58,49,44,34,109,97,120,95,112,114,111,100,117,99,116,95,105,100,34,58,48,44,34,101,110,101,114,121,103,95,105,110,102,105,110,105,116,101,95,108,101,102,116,95,116,105,109,101,115,34,58,48,44,34,115,112,101,110,100,95,109,111,110,101,121,34,58,48,44,34,116,117,116,111,114,105,97,108,95,111,110,34,58,116,114,117,101,44,34,116,111,107,101,110,34,58,48,44,34,102,105,114,115,116,95,116,108,118,34,58,116,114,117,101,44,34,102,98,95,102,105,114,115,116,95,97,119,97,114,100,34,58,102,97,108,115,101,44,34,115,101,114,118,101,114,95,100,97,121,95,105,100,120,34,58,49,56,54,51,52,44,34,115,101,114,118,101,114,95,109,97,105,108,95,105,100,34,58,48,44,34,115,116,97,114,34,58,50,44,34,100,114,95,105,110,102,105,110,105,116,101,34,58,48,44,34,101,110,101,114,103,121,34,58,53,44,34,98,104,95,105,110,102,105,110,105,116,101,34,58,48,44,34,100,105,97,109,111,110,100,34,58,48,44,34,101,110,101,114,103,121,95,105,110,102,105,110,105,116,101,34,58,48,44,34,103,111,108,100,34,58,49,51,48,55,44,34,108,111,103,105,110,95,100,97,121,95,99,111,117,110,116,34,58,49,44,34,103,111,108,100,95,114,101,34,58,48,44,34,98,100,95,105,110,102,105,110,105,116,101,34,58,48,44,34,108,118,95,105,100,120,34,58,54,44,34,100,114,95,105,110,102,105,110,105,116,101,95,108,101,102,116,95,115,101,99,34,58,48,44,34,99,97,116,95,99,111,105,110,34,58,48,44,34,115,101,108,102,95,114,111,111,109,95,115,116,97,114,115,34,58,48,44,34,97,100,95,114,101,109,97,105,110,105,110,103,95,110,117,109,34,58,48,44,34,110,101,119,112,108,97,121,101,114,95,115,105,103,110,105,110,95,99,111,109,112,108,101,116,101,34,58,48,44,34,108,97,115,116,95,108,111,103,105,110,95,100,97,121,95,105,110,100,101,120,34,58,34,48,48,55,34,44,34,111,114,100,101,114,95,105,100,95,108,105,115,116,34,58,91,93,44,34,101,110,101,114,103,121,95,114,101,99,111,118,101,114,95,104,97,108,118,101,34,58,48,44,34,109,97,105,110,95,100,101,99,111,114,97,116,101,95,115,116,117,102,102,34,58,49,48,51,125,44,34,100,98,95,105,116,101,109,95,108,105,115,116,34,58,91,91,34,105,116,101,109,95,117,105,100,34,44,34,105,116,101,109,95,110,117,109,34,44,34,105,116,101,109,95,116,105,100,34,93,44,91,49,44,49,48,48,49,44,52,44,49,48,48,54,93,44,91,50,44,49,48,48,50,44,52,44,49,48,48,55,93,44,91,51,44,49,48,48,51,44,52,44,49,48,48,56,93,44,91,52,44,49,48,48,52,44,52,44,49,48,49,53,93,44,91,53,44,49,48,48,53,44,52,44,49,48,49,54,93,44,91,54,44,49,48,48,54,44,52,44,49,48,49,55,93,93,44,34,100,98,95,115,116,117,102,102,95,99,104,105,108,100,95,118,105,115,105,98,108,101,34,58,91,93,44,34,100,98,95,99,97,116,95,99,104,97,114,97,99,116,101,114,34,58,123,52,48,48,49,58,91,49,44,50,93,125,125>>, - client_idx => 1, - client_incr => [], - %%[<<"{\"s\":{\"db_data_verify\":{\"m\":{\"upload_data_time\":1609998581,\"generate_index\":43}},\"db_monthcard\":{\"m\":{\"left_os_time\":1609998581,\"day_left_time\":36619}},\"db_role\":{\"m\":{\"play_time_sec\":7360,\"logout_time\":1609999202}}}}">>,<<"{\"s\":{\"db_level\":{\"m\":{\"lv_end_gold\":true,\"set_result\":1,\"level_state\":2,\"flag_lv\":6}},\"db_tutorial\":{\"m\":{56:true}},\"db_logger\":{\"m\":{\"seed\":2103574528}},\"db_data_verify\":{\"m\":{\"generate_index\":42}},\"db_role\":{\"m\":{\"play_time_sec\":6730,\"logout_time\":1609998572}}}}">>,<<"{\"s\":{\"db_data_verify\":{\"m\":{\"upload_data_time\":1609997794,\"generate_index\":41}},\"db_monthcard\":{\"m\":{\"left_os_time\":1609997794,\"day_left_time\":37406}},\"db_role\":{\"m\":{\"play_time_sec\":6111,\"logout_time\":1609997953}}}}">>,<<"{\"s\":{\"db_data_verify\":{\"m\":{\"generate_index\":40}},\"db_role\":{\"m\":{\"login_time\":1609997792,\"logout_time\":1609997792}}}}">>], - client_map => #{}, - cross_hammer => 4, - d_rockets => 4, - energy => 0, event_idx => 43, exchange => 4, game_time => 0, gold => 1307, hammer => 4, - house_data => <<>>, - key_data => #{ - '$map_tab' => key_data, - activity => #{'$map_tab' => act_dv}, - ad => #{21 => 0, 25 => 0, 53 => 0, 81 => 0, 83 => 0, 84 => 0, 86 => 0, 87 => 0, 104 => 0, '$map_tab' => ad_dv, day => 18634}, - award_uid => 0, cat_room => {2001, []}, - cats => <<"@">>, chapter => {0, 1, "gfe"}, - character => #{4001 => [2, 1]}, - feed => {0, 0, 0, 0, 0}, - guide_awards => [10012111, 10000], - icons => <<>>, - limit_room => {[], []}, - milk_bottle => 0, - ml => [], - special_back => #{}, - spoint => 0, - store => <<>>, - story_list => [10012, 10000], - styles => [] - }, - - lv => 6, - res_version => "0.9.5", role_id => 10010001, story => 2, sync_idx => 1, - token => 0, upload_time => 1609999206, verify_result => 0 - }. - -gc() -> - lists:foreach(fun(Pid) -> erlang:garbage_collect(Pid) end, erlang:processes()), - erlang:garbage_collect(). - -tt_w1(Term) -> - ?MEM_INFO_INIT(), - io_lib:format("~w", [Term]), - ?MEM_INFO_PRINT(0). - -tt_w2(Term) -> - ?MEM_INFO_INIT(), - eFmt:format("~w", [Term]), - ?MEM_INFO_PRINT(0). - -tt_w11() -> - ?MEM_INFO_INIT(), - io_lib:format("~w", [get_test()]), - ?MEM_INFO_PRINT(0). - -tt_w12() -> - ?MEM_INFO_INIT(), - eFmt:format("~w", [get_test()]), - ?MEM_INFO_PRINT(0). - -tt_w21() -> - ?MEM_INFO_INIT(), - io_lib:format("~w", [get_test2()]), - ?MEM_INFO_PRINT(0). - -tt_w22() -> - ?MEM_INFO_INIT(), - eFmt:format(<<"~w">>, [get_test2()]), - ?MEM_INFO_PRINT(0). - -tt_p1(Term) -> - ?MEM_INFO_INIT(), - io_lib:format("~p", [Term]), - ?MEM_INFO_PRINT(0). - -tt_p2(Term) -> - ?MEM_INFO_INIT(), - eFmt:format("~p", [Term]), - ?MEM_INFO_PRINT(0). - -tt_p11() -> - ?MEM_INFO_INIT(), - io_lib:format("~p", [get_test()]), - ?MEM_INFO_PRINT(0). - -tt_p12() -> - ?MEM_INFO_INIT(), - eFmt:format("~p", [get_test()]), - ?MEM_INFO_PRINT(0). - -tt_p21() -> - ?MEM_INFO_INIT(), - io_lib:format("~p", [get_test2()]), - ?MEM_INFO_PRINT(0). - -tt_p22() -> - ?MEM_INFO_INIT(), - eFmt:format(<<"~p">>, [get_test2()]), - ?MEM_INFO_PRINT(0). - --define(SQL_ROLE_CHAT_DATA_UPDATE, <<"update `role_chat` set `channels`='~s', `sensitive`=~p, `ban_times`=~p, `time`=~p where `role_id`=~p">>). - -tt_s1() -> - ?MEM_INFO_INIT(), - io_lib:format(?SQL_ROLE_CHAT_DATA_UPDATE, [<<"YYYY">>, [abcdef, 134, 423], {adfs, gfdgfg, "fdsfdsfs"}, #{aaaa => bbb, vvv => dddd}, self()]), - ?MEM_INFO_PRINT(0). - -tt_s2() -> - ?MEM_INFO_INIT(), - eFmt:format(?SQL_ROLE_CHAT_DATA_UPDATE, [<<"YYYY">>, [abcdef, 134, 423], {adfs, gfdgfg, "fdsfdsfs"}, #{aaaa => bbb, vvv => dddd}, self()]), - ?MEM_INFO_PRINT(0). - -for(0, _M, _F, _A) -> - ok; -for(N, M, F, A) -> - apply(M, F, A), - for(N - 1, M, F, A). - -tt_FP1(N) -> - ?MEM_INFO_INIT(), - for(N, io_lib, format, ["~p", [get_test2()]]), - ?MEM_INFO_PRINT(0). - -tt_FP2(N) -> - ?MEM_INFO_INIT(), - for(N, eFmt, format, [<<"~p">>, [get_test2()]]), - ?MEM_INFO_PRINT(0). - -tt_FW1(N) -> - ?MEM_INFO_INIT(), - for(N, io_lib, format, ["~w", [get_test2()]]), - ?MEM_INFO_PRINT(0). - -tt_FW2(N) -> - ?MEM_INFO_INIT(), - for(N, eFmt, format, [<<"~w">>, [get_test2()]]), - ?MEM_INFO_PRINT(0). - -tt_FS1(N) -> - ?MEM_INFO_INIT(), - for(N, io_lib, format, [?SQL_ROLE_CHAT_DATA_UPDATE, [<<"YYYY">>, [abcdef, 134, 423], {adfs, gfdgfg, "fdsfdsfs"}, #{aaaa => bbb, vvv => dddd}, self()]]), - ?MEM_INFO_PRINT(0). - -tt_FS2(N) -> - ?MEM_INFO_INIT(), - for(N, eFmt, format, [?SQL_ROLE_CHAT_DATA_UPDATE, [<<"YYYY">>, [abcdef, 134, 423], {adfs, gfdgfg, "fdsfdsfs"}, #{aaaa => bbb, vvv => dddd}, self()]]), - ?MEM_INFO_PRINT(0). - -tt_FM1(N) -> - ?MEM_INFO_INIT(), - for(N, eFmt, writeTerm, [get_test2(), -1, unicode]), - ?MEM_INFO_PRINT(0). - -tt_FM2(N) -> - ?MEM_INFO_INIT(), - for(N, eFmt, writeTerm, [get_test2(), -1, 120, unicode, true]), - ?MEM_INFO_PRINT(0). - - -tt_m1() -> - ?MEM_INFO_INIT(), - eFmt:writeTerm(get_test2(), -1, unicode), - ?MEM_INFO_PRINT(0). - -tt_m2() -> - ?MEM_INFO_INIT(), - eFmt:writeTerm(get_test2(), -1, 120, unicode, true), - ?MEM_INFO_PRINT(0). - -tt_Fmap1(N) -> - ?MEM_INFO_INIT(), - for(N, ?MODULE, writeMap1, [get_test2(), -1, unicode, <<"#{">>]), - ?MEM_INFO_PRINT(0). - -tt_Fmap2(N) -> - ?MEM_INFO_INIT(), - for(N, ?MODULE, writeMap2, [get_test2(), -1, unicode, <<"#{">>]), - ?MEM_INFO_PRINT(0). - - -writeMap1(Map, D, E, BinAcc) -> - if - D =:= 1 -> - <>; - true -> - writeMapBody1(maps:iterator(Map), D, E, BinAcc) - end. - -writeMapBody1(I, D, E, BinAcc) -> - if - D =:= 1 -> - <>; - true -> - case maps:next(I) of - {K, V, none} -> - < ", (eFmt:writeTerm(V, D, E))/binary, "}">>; - {K, V, NextI} -> - writeMapBody1(NextI, D - 1, E, < ", (eFmt:writeTerm(V, D, E))/binary, ",">>); - _ -> - <> - end - end. - -writeMap2(Map, D, E, BinAcc) -> - if - D =:= 1 -> - <>; - true -> - writeMapBody2(maps:iterator(Map), D, E, BinAcc) - end. - -writeMapBody2(I, D, E, BinAcc) -> - if - D =:= 1 -> - <>; - true -> - case maps:next(I) of - {K, V, none} -> - KeyTermBin = eFmt:writeTerm(K, -1, E), - ValueTermBin = eFmt:writeTerm(V, -1, E), - < ", ValueTermBin/binary, "}">>; - {K, V, NextI} -> - KeyTermBin = eFmt:writeTerm(K, -1, E), - ValueTermBin = eFmt:writeTerm(V, -1, E), - writeMapBody2(NextI, D - 1, E, < ", ValueTermBin/binary, ",">>); - _ -> - <> - end - end. - -tss1(K, V, E, BinAcc) -> - tss1(K, V, E, < ", (eFmt:writeTerm(V, -1, E))/binary, ",">>). - - -tss2(K, V, E, BinAcc) -> - KeyTermBin = eFmt:writeTerm(K, -1, E), - ValueTermBin = eFmt:writeTerm(V, -1, E), - tss1(K, V, E, < ", ValueTermBin/binary, ",">>). diff --git a/src/test/recon-2.5.1/recon_trace.erl b/src/test/recon-2.5.1/recon_trace.erl deleted file mode 100644 index a0bc574..0000000 --- a/src/test/recon-2.5.1/recon_trace.erl +++ /dev/null @@ -1,733 +0,0 @@ -%%% @author Fred Hebert -%%% [http://ferd.ca/] -%%% @doc -%%% `recon_trace' is a module that handles tracing in a safe manner for single -%%% Erlang nodes, currently for function calls only. Functionality includes: -%%% -%%%
    -%%%
  • Nicer to use interface (arguably) than `dbg' or trace BIFs.
  • -%%%
  • Protection against dumb decisions (matching all calls on a node -%%% being traced, for example)
  • -%%%
  • Adding safe guards in terms of absolute trace count or -%%% rate-limitting
  • -%%%
  • Nicer formatting than default traces
  • -%%%
-%%% -%%% == Tracing Erlang Code == -%%% -%%% The Erlang Trace BIFs allow to trace any Erlang code at all. They work in -%%% two parts: pid specifications, and trace patterns. -%%% -%%% Pid specifications let you decide which processes to target. They can be -%%% specific pids, `all' pids, `existing' pids, or `new' pids (those not -%%% spawned at the time of the function call). -%%% -%%% The trace patterns represent functions. Functions can be specified in two -%%% parts: specifying the modules, functions, and arguments, and then with -%%% Erlang match specifications to add constraints to arguments (see -%%% {@link calls/3} for details). -%%% -%%% What defines whether you get traced or not is the intersection of both: -%%% -%%% ``` -%%% _,--------,_ _,--------,_ -%%% ,-' `-,,-' `-, -%%% ,-' ,-' '-, `-, -%%% | Matching -' '- Matching | -%%% | Pids | Getting | Trace | -%%% | | Traced | Patterns | -%%% | -, ,- | -%%% '-, '-, ,-' ,-' -%%% '-,_ _,-''-,_ _,-' -%%% '--------' '--------' -%%% ''' -%%% -%%% If either the pid specification excludes a process or a trace pattern -%%% excludes a given call, no trace will be received. -%%% -%%% == Example Session == -%%% -%%% First let's trace the `queue:new' functions in any process: -%%% -%%% ``` -%%% 1> recon_trace:calls({queue, new, '_'}, 1). -%%% 1 -%%% 13:14:34.086078 <0.44.0> queue:new() -%%% Recon tracer rate limit tripped. -%%% ''' -%%% -%%% The limit was set to `1' trace message at most, and `recon' let us -%%% know when that limit was reached. -%%% -%%% Let's instead look for all the `queue:in/2' calls, to see what it is -%%% we're inserting in queues: -%%% -%%% ``` -%%% 2> recon_trace:calls({queue, in, 2}, 1). -%%% 1 -%%% 13:14:55.365157 <0.44.0> queue:in(a, {[],[]}) -%%% Recon tracer rate limit tripped. -%%% ''' -%%% -%%% In order to see the content we want, we should change the trace patterns -%%% to use a `fun' that matches on all arguments in a list (`_') and returns -%%% `return_trace()'. This last part will generate a second trace for each -%%% call that includes the return value: -%%% -%%% ``` -%%% 3> recon_trace:calls({queue, in, fun(_) -> return_trace() end}, 3). -%%% 1 -%%% -%%% 13:15:27.655132 <0.44.0> queue:in(a, {[],[]}) -%%% -%%% 13:15:27.655467 <0.44.0> queue:in/2 --> {[a],[]} -%%% -%%% 13:15:27.757921 <0.44.0> queue:in(a, {[],[]}) -%%% Recon tracer rate limit tripped. -%%% ''' -%%% -%%% Matching on argument lists can be done in a more complex manner: -%%% -%%% ``` -%%% 4> recon_trace:calls( -%%% 4> {queue, '_', fun([A,_]) when is_list(A); is_integer(A) andalso A > 1 -> return_trace() end}, -%%% 4> {10,100} -%%% 4> ). -%%% 32 -%%% -%%% 13:24:21.324309 <0.38.0> queue:in(3, {[],[]}) -%%% -%%% 13:24:21.371473 <0.38.0> queue:in/2 --> {[3],[]} -%%% -%%% 13:25:14.694865 <0.53.0> queue:split(4, {[10,9,8,7],[1,2,3,4,5,6]}) -%%% -%%% 13:25:14.695194 <0.53.0> queue:split/2 --> {{[4,3,2],[1]},{[10,9,8,7],[5,6]}} -%%% -%%% 5> recon_trace:clear(). -%%% ok -%%% ''' -%%% -%%% Note that in the pattern above, no specific function ('_') was -%%% matched against. Instead, the `fun' used restricted functions to those -%%% having two arguments, the first of which is either a list or an integer -%%% greater than `1'. -%%% -%%% The limit was also set using `{10,100}' instead of an integer, making the -%%% rate-limitting at 10 messages per 100 milliseconds, instead of an absolute -%%% value. -%%% -%%% Any tracing can be manually interrupted by calling `recon_trace:clear()', -%%% or killing the shell process. -%%% -%%% Be aware that extremely broad patterns with lax rate-limitting (or very -%%% high absolute limits) may impact your node's stability in ways -%%% `recon_trace' cannot easily help you with. -%%% -%%% In doubt, start with the most restrictive tracing possible, with low -%%% limits, and progressively increase your scope. -%%% -%%% See {@link calls/3} for more details and tracing possibilities. -%%% -%%% == Structure == -%%% -%%% This library is production-safe due to taking the following structure for -%%% tracing: -%%% -%%% ``` -%%% [IO/Group leader] <---------------------, -%%% | | -%%% [shell] ---> [tracer process] ----> [formatter] -%%% ''' -%%% -%%% The tracer process receives trace messages from the node, and enforces -%%% limits in absolute terms or trace rates, before forwarding the messages -%%% to the formatter. This is done so the tracer can do as little work as -%%% possible and never block while building up a large mailbox. -%%% -%%% The tracer process is linked to the shell, and the formatter to the -%%% tracer process. The formatter also traps exits to be able to handle -%%% all received trace messages until the tracer termination, but will then -%%% shut down as soon as possible. -%%% -%%% In case the operator is tracing from a remote shell which gets -%%% disconnected, the links between the shell and the tracer should make it -%%% so tracing is automatically turned off once you disconnect. -%%% -%%% If sending output to the Group Leader is not desired, you may specify -%%% a different pid() via the option `io_server' in the {@link calls/3} function. -%%% For instance to write the traces to a file you can do something like -%%% -%%% ``` -%%% 1> {ok, Dev} = file:open("/tmp/trace",[write]). -%%% 2> recon_trace:calls({queue, in, fun(_) -> return_trace() end}, 3, [{io_server, Dev}]). -%%% 1 -%%% 3> -%%% Recon tracer rate limit tripped. -%%% 4> file:close(Dev). -%%% ''' -%%% -%%% The only output still sent to the Group Leader is the rate limit being -%%% tripped, and any errors. The rest will be sent to the other IO -%%% server (see [http://erlang.org/doc/apps/stdlib/io_protocol.html]). -%%% -%%% == Record Printing == -%%% -%%% Thanks to code contributed by Bartek Górny, record printing can be added -%%% to traces by first importing records in an active session with -%%% `recon_rec:import([Module, ...])', after which the records declared in -%%% the module list will be supported. -%%% @end --module(recon_trace). - -%% API --export([clear/0, calls/2, calls/3]). - --export([format/1]). - -%% Internal exports --export([count_tracer/1, rate_tracer/2, formatter/5, format_trace_output/1, format_trace_output/2]). - --type matchspec() :: [{[term()] | '_', [term()], [term()]}]. --type shellfun() :: fun((_) -> term()). --type formatterfun() :: fun((_) -> iodata()). --type millisecs() :: non_neg_integer(). --type pidspec() :: all | existing | new | recon:pid_term(). --type max_traces() :: non_neg_integer(). --type max_rate() :: {max_traces(), millisecs()}. - -%% trace options --type options() :: [{pid, pidspec() | [pidspec(), ...]} % default: all -| {timestamp, formatter | trace} % default: formatter -| {args, args | arity} % default: args -| {io_server, pid()} % default: group_leader() -| {formatter, formatterfun()} % default: internal formatter -| return_to | {return_to, boolean()} % default: false -%% match pattern options -| {scope, global | local} % default: global -]. - --type mod() :: '_' | module(). --type fn() :: '_' | atom(). --type args() :: '_' | 0..255 | return_trace | matchspec() | shellfun(). --type tspec() :: {mod(), fn(), args()}. --type max() :: max_traces() | max_rate(). --type num_matches() :: non_neg_integer(). - --export_type([mod/0, fn/0, args/0, tspec/0, num_matches/0, options/0, - max_traces/0, max_rate/0]). - -%%%%%%%%%%%%%% -%%% PUBLIC %%% -%%%%%%%%%%%%%% - -%% @doc Stops all tracing at once. --spec clear() -> ok. -clear() -> - erlang:trace(all, false, [all]), - erlang:trace_pattern({'_', '_', '_'}, false, [local, meta, call_count, call_time]), - erlang:trace_pattern({'_', '_', '_'}, false, []), % unsets global - maybe_kill(recon_trace_tracer), - maybe_kill(recon_trace_formatter), - ok. - -%% @equiv calls({Mod, Fun, Args}, Max, []) --spec calls(tspec() | [tspec(), ...], max()) -> num_matches(). -calls({Mod, Fun, Args}, Max) -> - calls([{Mod, Fun, Args}], Max, []); -calls(TSpecs = [_ | _], Max) -> - calls(TSpecs, Max, []). - -%% @doc Allows to set trace patterns and pid specifications to trace -%% function calls. -%% -%% The basic calls take the trace patterns as tuples of the form -%% `{Module, Function, Args}' where: -%% -%%
    -%%
  • `Module' is any atom representing a module
  • -%%
  • `Function' is any atom representing a function, or the wildcard -%% '_'
  • -%%
  • `Args' is either the arity of a function (`0..255'), a wildcard -%% pattern ('_'), a -%% match specification, -%% or a function from a shell session that can be transformed into -%% a match specification
  • -%%
-%% -%% There is also an argument specifying either a maximal count (a number) -%% of trace messages to be received, or a maximal frequency (`{Num, Millisecs}'). -%% -%% Here are examples of things to trace: -%% -%%
    -%%
  • All calls from the `queue' module, with 10 calls printed at most: -%% ``recon_trace:calls({queue, '_', '_'}, 10)''
  • -%%
  • All calls to `lists:seq(A,B)', with 100 calls printed at most: -%% `recon_trace:calls({lists, seq, 2}, 100)'
  • -%%
  • All calls to `lists:seq(A,B)', with 100 calls per second at most: -%% `recon_trace:calls({lists, seq, 2}, {100, 1000})'
  • -%%
  • All calls to `lists:seq(A,B,2)' (all sequences increasing by two) -%% with 100 calls at most: -%% `recon_trace:calls({lists, seq, fun([_,_,2]) -> ok end}, 100)'
  • -%%
  • All calls to `iolist_to_binary/1' made with a binary as an argument -%% already (kind of useless conversion!): -%% `recon_trace:calls({erlang, iolist_to_binary, fun([X]) when is_binary(X) -> ok end}, 10)'
  • -%%
  • Calls to the queue module only in a given process `Pid', at a rate -%% of 50 per second at most: -%% ``recon_trace:calls({queue, '_', '_'}, {50,1000}, [{pid, Pid}])''
  • -%%
  • Print the traces with the function arity instead of literal arguments: -%% `recon_trace:calls(TSpec, Max, [{args, arity}])'
  • -%%
  • Matching the `filter/2' functions of both `dict' and `lists' modules, -%% across new processes only: -%% `recon_trace:calls([{dict,filter,2},{lists,filter,2}], 10, [{pid, new}])'
  • -%%
  • Tracing the `handle_call/3' functions of a given module for all new processes, -%% and those of an existing one registered with `gproc': -%% `recon_trace:calls({Mod,handle_call,3}, {10,100}, [{pid, [{via, gproc, Name}, new]}'
  • -%%
  • Show the result of a given function call: -%% `recon_trace:calls({Mod,Fun,fun(_) -> return_trace() end}, Max, Opts)' -%% or -%% ``recon_trace:calls({Mod,Fun,[{'_', [], [{return_trace}]}]}, Max, Opts)'', -%% the important bit being the `return_trace()' call or the -%% `{return_trace}' match spec value. -%% A short-hand version for this pattern of 'match anything, trace everything' -%% for a function is `recon_trace:calls({Mod, Fun, return_trace})'.
  • -%%
-%% -%% There's a few more combination possible, with multiple trace patterns per call, and more -%% options: -%% -%%
    -%%
  • `{pid, PidSpec}': which processes to trace. Valid options is any of -%% `all', `new', `existing', or a process descriptor (`{A,B,C}', -%% `""', an atom representing a name, `{global, Name}', -%% `{via, Registrar, Name}', or a pid). It's also possible to specify -%% more than one by putting them in a list.
  • -%%
  • `{timestamp, formatter | trace}': by default, the formatter process -%% adds timestamps to messages received. If accurate timestamps are -%% required, it's possible to force the usage of timestamps within -%% trace messages by adding the option `{timestamp, trace}'.
  • -%%
  • `{args, arity | args}': whether to print arity in function calls -%% or their (by default) literal representation.
  • -%%
  • `{scope, global | local}': by default, only 'global' (fully qualified -%% function calls) are traced, not calls made internally. To force tracing -%% of local calls, pass in `{scope, local}'. This is useful whenever -%% you want to track the changes of code in a process that isn't called -%% with `Module:Fun(Args)', but just `Fun(Args)'.
  • -%%
  • `{formatter, fun(Term) -> io_data() end}': override the default -%% formatting functionality provided by recon.
  • -%%
  • `{io_server, pid() | atom()}': by default, recon logs to the current -%% group leader, usually the shell. This option allows to redirect -%% trace output to a different IO server (such as a file handle).
  • -%%
  • `return_to': If this option is set (in conjunction with the match -%% option `{scope, local}'), the function to which the value is returned -%% is output in a trace. Note that this is distinct from giving the -%% *caller* since exception handling or calls in tail position may -%% hide the original caller.
  • -%%
-%% -%% Also note that putting extremely large `Max' values (i.e. `99999999' or -%% `{10000,1}') will probably negate most of the safe-guarding this library -%% does and be dangerous to your node. Similarly, tracing extremely large -%% amounts of function calls (all of them, or all of `io' for example) -%% can be risky if more trace messages are generated than any process on -%% the node could ever handle, despite the precautions taken by this library. -%% @end --spec calls(tspec() | [tspec(), ...], max(), options()) -> num_matches(). - -calls({Mod, Fun, Args}, Max, Opts) -> - calls([{Mod, Fun, Args}], Max, Opts); -calls(TSpecs = [_ | _], {Max, Time}, Opts) -> - Pid = setup(rate_tracer, [Max, Time], - validate_formatter(Opts), validate_io_server(Opts)), - trace_calls(TSpecs, Pid, Opts); -calls(TSpecs = [_ | _], Max, Opts) -> - Pid = setup(count_tracer, [Max], - validate_formatter(Opts), validate_io_server(Opts)), - trace_calls(TSpecs, Pid, Opts). - -%%%%%%%%%%%%%%%%%%%%%%% -%%% PRIVATE EXPORTS %%% -%%%%%%%%%%%%%%%%%%%%%%% -%% @private Stops when N trace messages have been received -count_tracer(0) -> - exit(normal); -count_tracer(N) -> - receive - Msg -> - recon_trace_formatter ! Msg, - count_tracer(N - 1) - end. - -%% @private Stops whenever the trace message rates goes higher than -%% `Max' messages in `Time' milliseconds. Note that if the rate -%% proposed is higher than what the IO system of the formatter -%% can handle, this can still put a node at risk. -%% -%% It is recommended to try stricter rates to begin with. -rate_tracer(Max, Time) -> rate_tracer(Max, Time, 0, os:timestamp()). - -rate_tracer(Max, Time, Count, Start) -> - receive - Msg -> - recon_trace_formatter ! Msg, - Now = os:timestamp(), - Delay = timer:now_diff(Now, Start) div 1000, - if Delay > Time -> rate_tracer(Max, Time, 0, Now) - ; Max > Count -> rate_tracer(Max, Time, Count + 1, Start) - ; Max =:= Count -> exit(normal) - end - end. - -%% @private Formats traces to be output -formatter(Tracer, Parent, Ref, FormatterFun, IOServer) -> - process_flag(trap_exit, true), - link(Tracer), - Parent ! {Ref, linked}, - formatter(Tracer, IOServer, FormatterFun). - -formatter(Tracer, IOServer, FormatterFun) -> - receive - {'EXIT', Tracer, normal} -> - io:format("Recon tracer rate limit tripped.~n"), - exit(normal); - {'EXIT', Tracer, Reason} -> - exit(Reason); - TraceMsg -> - io:format(IOServer, FormatterFun(TraceMsg), []), - formatter(Tracer, IOServer, FormatterFun) - end. - - -%%%%%%%%%%%%%%%%%%%%%%% -%%% SETUP FUNCTIONS %%% -%%%%%%%%%%%%%%%%%%%%%%% - -%% starts the tracer and formatter processes, and -%% cleans them up before each call. -setup(TracerFun, TracerArgs, FormatterFun, IOServer) -> - clear(), - Ref = make_ref(), - Tracer = spawn_link(?MODULE, TracerFun, TracerArgs), - register(recon_trace_tracer, Tracer), - Format = spawn(?MODULE, formatter, [Tracer, self(), Ref, FormatterFun, IOServer]), - register(recon_trace_formatter, Format), - receive - {Ref, linked} -> Tracer - after 5000 -> - error(setup_failed) - end. - -%% Sets the traces in action -trace_calls(TSpecs, Pid, Opts) -> - {PidSpecs, TraceOpts, MatchOpts} = validate_opts(Opts), - Matches = [begin - {Arity, Spec} = validate_tspec(Mod, Fun, Args), - erlang:trace_pattern({Mod, Fun, Arity}, Spec, MatchOpts) - end || {Mod, Fun, Args} <- TSpecs], - [erlang:trace(PidSpec, true, [call, {tracer, Pid} | TraceOpts]) - || PidSpec <- PidSpecs], - lists:sum(Matches). - - -%%%%%%%%%%%%%%%%%% -%%% VALIDATION %%% -%%%%%%%%%%%%%%%%%% - -validate_opts(Opts) -> - PidSpecs = validate_pid_specs(proplists:get_value(pid, Opts, all)), - Scope = proplists:get_value(scope, Opts, global), - TraceOpts = case proplists:get_value(timestamp, Opts, formatter) of - formatter -> []; - trace -> [timestamp] - end ++ - case proplists:get_value(args, Opts, args) of - args -> []; - arity -> [arity] - end ++ - case proplists:get_value(return_to, Opts, undefined) of - true when Scope =:= local -> - [return_to]; - true when Scope =:= global -> - io:format("Option return_to only works with option {scope, local}~n"), - %% Set it anyway - [return_to]; - _ -> - [] - end, - MatchOpts = [Scope], - {PidSpecs, TraceOpts, MatchOpts}. - -%% Support the regular specs, but also allow `recon:pid_term()' and lists -%% of further pid specs. --spec validate_pid_specs(pidspec() | [pidspec(), ...]) -> - [all | new | existing | pid(), ...]. -validate_pid_specs(all) -> [all]; -validate_pid_specs(existing) -> [existing]; -validate_pid_specs(new) -> [new]; -validate_pid_specs([Spec]) -> validate_pid_specs(Spec); -validate_pid_specs(PidTerm = [Spec | Rest]) -> - %% can be "" or [pidspec()] - try - [recon_lib:term_to_pid(PidTerm)] - catch - error:function_clause -> - validate_pid_specs(Spec) ++ validate_pid_specs(Rest) - end; -validate_pid_specs(PidTerm) -> - %% has to be `recon:pid_term()'. - [recon_lib:term_to_pid(PidTerm)]. - -validate_tspec(Mod, Fun, Args) when is_function(Args) -> - validate_tspec(Mod, Fun, fun_to_ms(Args)); -%% helper to save typing for common actions -validate_tspec(Mod, Fun, return_trace) -> - validate_tspec(Mod, Fun, [{'_', [], [{return_trace}]}]); -validate_tspec(Mod, Fun, Args) -> - BannedMods = ['_', ?MODULE, io, lists], - %% The banned mod check can be bypassed by using - %% match specs if you really feel like being dumb. - case {lists:member(Mod, BannedMods), Args} of - {true, '_'} -> error({dangerous_combo, {Mod, Fun, Args}}); - {true, []} -> error({dangerous_combo, {Mod, Fun, Args}}); - _ -> ok - end, - case Args of - '_' -> {'_', true}; - _ when is_list(Args) -> {'_', Args}; - _ when Args >= 0, Args =< 255 -> {Args, true} - end. - -validate_formatter(Opts) -> - case proplists:get_value(formatter, Opts) of - F when is_function(F, 1) -> F; - _ -> fun format/1 - end. - -validate_io_server(Opts) -> - proplists:get_value(io_server, Opts, group_leader()). - -%%%%%%%%%%%%%%%%%%%%%%%% -%%% TRACE FORMATTING %%% -%%%%%%%%%%%%%%%%%%%%%%%% -%% Thanks Geoff Cant for the foundations for this. -format(TraceMsg) -> - {Type, Pid, {Hour, Min, Sec}, TraceInfo} = extract_info(TraceMsg), - {FormatStr, FormatArgs} = case {Type, TraceInfo} of - %% {trace, Pid, 'receive', Msg} - {'receive', [Msg]} -> - {"< ~p", [Msg]}; - %% {trace, Pid, send, Msg, To} - {send, [Msg, To]} -> - {" > ~p: ~p", [To, Msg]}; - %% {trace, Pid, send_to_non_existing_process, Msg, To} - {send_to_non_existing_process, [Msg, To]} -> - {" > (non_existent) ~p: ~p", [To, Msg]}; - %% {trace, Pid, call, {M, F, Args}} - {call, [{M, F, Args}]} -> - {"~p:~p~s", [M, F, format_args(Args)]}; - %% {trace, Pid, call, {M, F, Args}, Msg} - {call, [{M, F, Args}, Msg]} -> - {"~p:~p~s ~s", [M, F, format_args(Args), format_trace_output(Msg)]}; - %% {trace, Pid, return_to, {M, F, Arity}} - {return_to, [{M, F, Arity}]} -> - {" '--> ~p:~p/~p", [M, F, Arity]}; - %% {trace, Pid, return_from, {M, F, Arity}, ReturnValue} - {return_from, [{M, F, Arity}, Return]} -> - {"~p:~p/~p --> ~s", [M, F, Arity, format_trace_output(Return)]}; - %% {trace, Pid, exception_from, {M, F, Arity}, {Class, Value}} - {exception_from, [{M, F, Arity}, {Class, Val}]} -> - {"~p:~p/~p ~p ~p", [M, F, Arity, Class, Val]}; - %% {trace, Pid, spawn, Spawned, {M, F, Args}} - {spawn, [Spawned, {M, F, Args}]} -> - {"spawned ~p as ~p:~p~s", [Spawned, M, F, format_args(Args)]}; - %% {trace, Pid, exit, Reason} - {exit, [Reason]} -> - {"EXIT ~p", [Reason]}; - %% {trace, Pid, link, Pid2} - {link, [Linked]} -> - {"link(~p)", [Linked]}; - %% {trace, Pid, unlink, Pid2} - {unlink, [Linked]} -> - {"unlink(~p)", [Linked]}; - %% {trace, Pid, getting_linked, Pid2} - {getting_linked, [Linker]} -> - {"getting linked by ~p", [Linker]}; - %% {trace, Pid, getting_unlinked, Pid2} - {getting_unlinked, [Unlinker]} -> - {"getting unlinked by ~p", [Unlinker]}; - %% {trace, Pid, register, RegName} - {register, [Name]} -> - {"registered as ~p", [Name]}; - %% {trace, Pid, unregister, RegName} - {unregister, [Name]} -> - {"no longer registered as ~p", [Name]}; - %% {trace, Pid, in, {M, F, Arity} | 0} - {in, [{M, F, Arity}]} -> - {"scheduled in for ~p:~p/~p", [M, F, Arity]}; - {in, [0]} -> - {"scheduled in", []}; - %% {trace, Pid, out, {M, F, Arity} | 0} - {out, [{M, F, Arity}]} -> - {"scheduled out from ~p:~p/~p", [M, F, Arity]}; - {out, [0]} -> - {"scheduled out", []}; - %% {trace, Pid, gc_start, Info} - {gc_start, [Info]} -> - HeapSize = proplists:get_value(heap_size, Info), - OldHeapSize = proplists:get_value(old_heap_size, Info), - MbufSize = proplists:get_value(mbuf_size, Info), - {"gc beginning -- heap ~p bytes", - [HeapSize + OldHeapSize + MbufSize]}; - %% {trace, Pid, gc_end, Info} - {gc_end, [Info]} -> - HeapSize = proplists:get_value(heap_size, Info), - OldHeapSize = proplists:get_value(old_heap_size, Info), - MbufSize = proplists:get_value(mbuf_size, Info), - {"gc finished -- heap ~p bytes", - [HeapSize + OldHeapSize + MbufSize]}; - _ -> - {"unknown trace type ~p -- ~p", [Type, TraceInfo]} - end, - io_lib:format("~n~p:~p:~9.6.0f ~p " ++ FormatStr ++ "~n", - [Hour, Min, Sec, Pid] ++ FormatArgs). - -extract_info(TraceMsg) -> - case tuple_to_list(TraceMsg) of - [trace_ts, Pid, Type | Info] -> - {TraceInfo, [Timestamp]} = lists:split(length(Info) - 1, Info), - {Type, Pid, to_hms(Timestamp), TraceInfo}; - [trace, Pid, Type | TraceInfo] -> - {Type, Pid, to_hms(os:timestamp()), TraceInfo} - end. - -to_hms(Stamp = {_, _, Micro}) -> - {_, {H, M, Secs}} = calendar:now_to_local_time(Stamp), - Seconds = Secs rem 60 + (Micro / 1000000), - {H, M, Seconds}; -to_hms(_) -> - {0, 0, 0}. - -format_args(Arity) when is_integer(Arity) -> - [$/, integer_to_list(Arity)]; -format_args(Args) when is_list(Args) -> - [$(, join(", ", [format_trace_output(Arg) || Arg <- Args]), $)]. - - -%% @doc formats call arguments and return values - most types are just printed out, except for -%% tuples recognised as records, which mimic the source code syntax -%% @end -format_trace_output(Args) -> - format_trace_output(recon_rec:is_active(), recon_map:is_active(), Args). - -format_trace_output(Recs, Args) -> - format_trace_output(Recs, recon_map:is_active(), Args). - -format_trace_output(true, _, Args) when is_tuple(Args) -> - recon_rec:format_tuple(Args); -format_trace_output(false, true, Args) when is_tuple(Args) -> - format_tuple(false, true, Args); -format_trace_output(Recs, Maps, Args) when is_list(Args), Recs orelse Maps -> - case io_lib:printable_list(Args) of - true -> - io_lib:format("~p", [Args]); - false -> - format_maybe_improper_list(Recs, Maps, Args) - end; -format_trace_output(Recs, true, Args) when is_map(Args) -> - {Label, Map} = case recon_map:process_map(Args) of - {L, M} -> {atom_to_list(L), M}; - M -> {"", M} - end, - ItemList = maps:to_list(Map), - [Label, - "#{", - join(", ", [format_kv(Recs, true, Key, Val) || {Key, Val} <- ItemList]), - "}"]; -format_trace_output(Recs, false, Args) when is_map(Args) -> - ItemList = maps:to_list(Args), - ["#{", - join(", ", [format_kv(Recs, false, Key, Val) || {Key, Val} <- ItemList]), - "}"]; -format_trace_output(_, _, Args) -> - io_lib:format("~p", [Args]). - -format_kv(Recs, Maps, Key, Val) -> - [format_trace_output(Recs, Maps, Key), "=>", format_trace_output(Recs, Maps, Val)]. - - -format_tuple(Recs, Maps, Tup) -> - [${ | format_tuple_(Recs, Maps, tuple_to_list(Tup))]. - -format_tuple_(_Recs, _Maps, []) -> - "}"; -format_tuple_(Recs, Maps, [H | T]) -> - [format_trace_output(Recs, Maps, H), $,, - format_tuple_(Recs, Maps, T)]. - - -format_maybe_improper_list(Recs, Maps, List) -> - [$[ | format_maybe_improper_list_(Recs, Maps, List)]. - -format_maybe_improper_list_(_, _, []) -> - "]"; -format_maybe_improper_list_(Recs, Maps, [H | []]) -> - [format_trace_output(Recs, Maps, H), $]]; -format_maybe_improper_list_(Recs, Maps, [H | T]) when is_list(T) -> - [format_trace_output(Recs, Maps, H), $,, - format_maybe_improper_list_(Recs, Maps, T)]; -format_maybe_improper_list_(Recs, Maps, [H | T]) when not is_list(T) -> - %% Handling improper lists - [format_trace_output(Recs, Maps, H), $|, - format_trace_output(Recs, Maps, T), $]]. - - -%%%%%%%%%%%%%%% -%%% HELPERS %%% -%%%%%%%%%%%%%%% - -maybe_kill(Name) -> - case whereis(Name) of - undefined -> - ok; - Pid -> - unlink(Pid), - exit(Pid, kill), - wait_for_death(Pid, Name) - end. - -wait_for_death(Pid, Name) -> - case is_process_alive(Pid) orelse whereis(Name) =:= Pid of - true -> - timer:sleep(10), - wait_for_death(Pid, Name); - false -> - ok - end. - -%% Borrowed from dbg -fun_to_ms(ShellFun) when is_function(ShellFun) -> - case erl_eval:fun_data(ShellFun) of - {fun_data, ImportList, Clauses} -> - case ms_transform:transform_from_shell( - dbg, Clauses, ImportList) of - {error, [{_, [{_, _, Code} | _]} | _], _} -> - io:format("Error: ~s~n", - [ms_transform:format_error(Code)]), - {error, transform_error}; - Else -> - Else - end; - false -> - exit(shell_funs_only) - end. - - --ifdef(OTP_RELEASE). --spec join(term(), [term()]) -> [term()]. -join(Sep, List) -> - lists:join(Sep, List). --else. --spec join(string(), [string()]) -> string(). -join(Sep, List) -> - string:join(List, Sep). --endif. diff --git a/src/test/recon-2.5.1/utTc.erl b/src/test/recon-2.5.1/utTc.erl deleted file mode 100644 index 3d11566..0000000 --- a/src/test/recon-2.5.1/utTc.erl +++ /dev/null @@ -1,184 +0,0 @@ --module(utTc). - --compile(inline). --compile({inline_size, 128}). - --export([ - tc/1 - , tc/2 - , tc/3 - , ts/4 - , tm/5 - , test/1 -]). - -%% Measure the execution time (in nanoseconds) for Fun(). --spec tc(Fun :: function()) -> {Time :: integer(), Value :: term()}. -tc(F) -> - T1 = erlang:monotonic_time(), - Val = F(), - T2 = erlang:monotonic_time(), - Time = erlang:convert_time_unit(T2 - T1, native, nanosecond), - {Time, Val}. - -%% Measure the execution time (in nanoseconds) for Fun(Args). --spec tc(Fun :: function(), Arguments :: [term()]) -> {Time :: integer(), Value :: term()}. -tc(F, A) -> - T1 = erlang:monotonic_time(), - Val = apply(F, A), - T2 = erlang:monotonic_time(), - Time = erlang:convert_time_unit(T2 - T1, native, nanosecond), - {Time, Val}. - -%% Measure the execution time (in nanoseconds) for an MFA. --spec tc(Module :: module(), Function :: atom(), Arguments :: [term()]) -> {Time :: integer(), Value :: term()}. -tc(M, F, A) -> - T1 = erlang:monotonic_time(), - Val = apply(M, F, A), - T2 = erlang:monotonic_time(), - Time = erlang:convert_time_unit(T2 - T1, native, nanosecond), - {Time, Val}. - -%% 单进程循环测试:LoopTimes是循环次数 -%% utTc:ts(LoopTimes, Module, Function, ArgsList). -%% 多进程并发测试:SpawnProcessesCount是并发的进程数 LoopTimes是循环次数 -%% utTc:tm(ProcessesCount, LoopTimes, Module, Function, ArgsList). - -doTc(M, F, A) -> - T1 = erlang:monotonic_time(), - apply(M, F, A), - T2 = erlang:monotonic_time(), - erlang:convert_time_unit(T2 - T1, native, nanosecond). - -distribution(List, Aver) -> - distribution(List, Aver, 0, 0). -distribution([H | T], Aver, Greater, Less) -> - case H > Aver of - true -> - distribution(T, Aver, Greater + 1, Less); - false -> - distribution(T, Aver, Greater, Less + 1) - end; -distribution([], _Aver, Greater, Less) -> - {Greater, Less}. - -%% =================================================================== -%% test: one process test N times -%% =================================================================== -ts(LoopTime, M, F, A) -> - {Max, Min, Sum, Aver, Greater, Less} = loopTs(LoopTime, M, F, A, LoopTime, 0, 0, 0, []), - io:format("=====================~n"), - <<_:16, ArgsStr/binary>> = << <<", ", (iolist_to_binary(io_lib:format("~p", [OArg], [{chars_limit, 80}])))/binary>> || OArg <- A>>, - io:format("execute ~p:~p(~s).~n", [M, F, ArgsStr]), - io:format("execute LoopTime:~p~n", [LoopTime]), - io:format("MaxTime: ~10s(ns) ~10s(s)~n", [integer_to_binary(Max), float_to_binary(Max / 1000000000, [{decimals, 6}, compact])]), - io:format("MinTime: ~10s(ns) ~10s(s)~n", [integer_to_binary(Min), float_to_binary(Min / 1000000000, [{decimals, 6}, compact])]), - io:format("SumTime: ~10s(ns) ~10s(s)~n", [integer_to_binary(Sum), float_to_binary(Sum / 1000000000, [{decimals, 6}, compact])]), - io:format("AvgTime: ~10s(ns) ~10s(s)~n", [float_to_binary(Aver, [{decimals, 6}, compact]), float_to_binary(Aver / 1000000000, [{decimals, 6}, compact])]), - io:format("Grar : ~10s(cn) ~10s(~s)~n", [integer_to_binary(Greater), float_to_binary(Greater / LoopTime, [{decimals, 2}]), <<"%">>]), - io:format("Less : ~10s(cn) ~10s(~s)~n", [integer_to_binary(Less), float_to_binary(Less / LoopTime, [{decimals, 2}]), <<"%">>]), - io:format("=====================~n"). - - -loopTs(0, _M, _F, _A, LoopTime, Max, Min, Sum, List) -> - Aver = Sum / LoopTime, - {Greater, Less} = distribution(List, Aver), - {Max, Min, Sum, Aver, Greater, Less}; -loopTs(Index, M, F, A, LoopTime, Max, Min, Sum, List) -> - Nanosecond = doTc(M, F, A), - NewSum = Sum + Nanosecond, - if - Max == 0 -> - NewMax = NewMin = Nanosecond; - Max < Nanosecond -> - NewMax = Nanosecond, - NewMin = Min; - Min > Nanosecond -> - NewMax = Max, - NewMin = Nanosecond; - true -> - NewMax = Max, - NewMin = Min - end, - loopTs(Index - 1, M, F, A, LoopTime, NewMax, NewMin, NewSum, [Nanosecond | List]). - - -%% =================================================================== -%% Concurrency test: N processes each test one time -%% =================================================================== - -tm(ProcCnt, LoopTime, M, F, A) -> - loopSpawn(ProcCnt, M, F, A, self(), LoopTime), - {Max, Min, Sum, Aver, Greater, Less} = collector(ProcCnt, 0, 0, 0, ProcCnt, []), - io:format("=====================~n"), - <<_:16, ArgsStr/binary>> = << <<", ", (iolist_to_binary(io_lib:format("~p", [OArg], [{chars_limit, 80}])))/binary>> || OArg <- A>>, - io:format("execute ~p:~p(~s).~n", [M, F, ArgsStr]), - io:format("execute LoopTime:~p~n", [LoopTime]), - io:format("execute ProcCnts:~p~n", [ProcCnt]), - io:format("PMaxTime: ~10s(ns) ~10s(s)~n", [integer_to_binary(Max), float_to_binary(Max / 1000000000, [{decimals, 6}, compact])]), - io:format("PMinTime: ~10s(ns) ~10s(s)~n", [integer_to_binary(Min), float_to_binary(Min / 1000000000, [{decimals, 6}, compact])]), - io:format("PSumTime: ~10s(ns) ~10s(s)~n", [integer_to_binary(Sum), float_to_binary(Sum / 1000000000, [{decimals, 6}, compact])]), - io:format("PAvgTime: ~10s(ns) ~10s(s)~n", [float_to_binary(Aver, [{decimals, 6}, compact]), float_to_binary(Aver / 1000000000, [{decimals, 6}, compact])]), - io:format("FAvgTime: ~10s(ns) ~10s(s)~n", [float_to_binary(Aver / LoopTime, [{decimals, 6}, compact]), float_to_binary(Aver / LoopTime / 1000000000, [{decimals, 6}, compact])]), - io:format("PGrar : ~10s(cn) ~10s(~s)~n", [integer_to_binary(Greater), float_to_binary(Greater / ProcCnt, [{decimals, 2}]), <<"%">>]), - io:format("PLess : ~10s(cn) ~10s(~s)~n", [integer_to_binary(Less), float_to_binary(Less / ProcCnt, [{decimals, 2}]), <<"%">>]), - io:format("=====================~n"). - - -loopSpawn(0, _, _, _, _, _) -> - ok; -loopSpawn(ProcCnt, M, F, A, CollectorPid, LoopTime) -> - spawn_link(fun() -> worker(LoopTime, M, F, A, CollectorPid) end), - loopSpawn(ProcCnt - 1, M, F, A, CollectorPid, LoopTime). - -collector(0, Max, Min, Sum, ProcCnt, List) -> - Aver = Sum / ProcCnt, - {Greater, Less} = distribution(List, Aver), - {Max, Min, Sum, Aver, Greater, Less}; -collector(Index, Max, Min, Sum, ProcCnt, List) -> - receive - {result, Nanosecond} -> - NewSum = Sum + Nanosecond, - if - Max == 0 -> - NewMax = NewMin = Nanosecond; - Max < Nanosecond -> - NewMax = Nanosecond, - NewMin = Min; - Min > Nanosecond -> - NewMax = Max, - NewMin = Nanosecond; - true -> - NewMax = Max, - NewMin = Min - end, - collector(Index - 1, NewMax, NewMin, NewSum, ProcCnt, [Nanosecond | List]) - after 1800000 -> - io:format("execute time out~n"), - ok - end. - -worker(LoopTime, M, F, A, CollectorPid) -> - SumTime = loopTm(LoopTime, M, F, A, 0), - CollectorPid ! {result, SumTime}. - -loopTm(0, _, _, _, SumTime) -> - SumTime; -loopTm(LoopTime, M, F, A, SumTime) -> - Microsecond = doTc(M, F, A), - loopTm(LoopTime - 1, M, F, A, SumTime + Microsecond). - -test(N) -> - M1 = erlang:monotonic_time(), - timer:sleep(N), - M2 = erlang:monotonic_time(), - Time = erlang:convert_time_unit(M2 - M1, native, nanosecond), - io:format("IMY******************111 ~p~n", [Time]), - - S1 = erlang:system_time(nanosecond), - timer:sleep(N), - S2 = erlang:system_time(nanosecond), - io:format("IMY******************222 ~p~n", [S2 - S1]). - - -