diff --git a/include/eFmt.hrl b/include/eFmt.hrl index 5f0cec2..2687134 100644 --- a/include/eFmt.hrl +++ b/include/eFmt.hrl @@ -1,3 +1,6 @@ +%% pretty 模式下 每行打印的字符数 +-define(LineCCnt, 120). + -define(base(Precision), case Precision of none -> 10; _ -> Precision end). %% 三元表达式 -define(IIF(Cond, Ret1, Ret2), (case Cond of true -> Ret1; _ -> Ret2 end)). @@ -13,3 +16,4 @@ , strings :: boolean() %% 如果存在修饰符l,则将 string设置为false。 }). + diff --git a/src/eFmt.erl b/src/eFmt.erl index 89de205..7ae94be 100644 --- a/src/eFmt.erl +++ b/src/eFmt.erl @@ -105,7 +105,7 @@ write(Term, Depth) -> write(Term, Depth, IsPretty) -> case IsPretty of true -> - eFmtPretty:pPrint(Term, 1, 120, Depth); + eFmtPretty:pPrint(Term, 1, ?LineCCnt, Depth); _ -> writeTerm(Term, Depth, latin1) end. @@ -118,8 +118,33 @@ write(Term, Depth, Encoding, CharsLimit) -> CharsLimit < 0 -> writeTerm(Term, Depth, Encoding); true -> - If = eFmtPretty:pIntermediate(Term, Depth, CharsLimit, no_fun, Encoding, _Str = false), - eFmtPretty:pWrite(If) + BinTerm = writeTerm(Term, Depth, ?LineCCnt, Encoding, false), + BinTermSize = erlang:byte_size(BinTerm), + if + CharsLimit < 0 -> + BinTerm; + BinTermSize > CharsLimit -> + <<(binary:part(BinTerm, 0, CharsLimit))/binary, "...">>; + true -> + BinTerm + end + end. + +write(Term, Depth, Width, CharsLimit, Encoding, Strings) -> + if + Depth =:= 0 orelse CharsLimit =:= 0 -> + <<"...">>; + true -> + BinTerm = writeTerm(Term, Depth, Width, Encoding, Strings), + BinTermSize = erlang:byte_size(BinTerm), + if + CharsLimit < 0 -> + BinTerm; + BinTermSize > CharsLimit -> + <<(binary:part(BinTerm, 0, CharsLimit))/binary, "...">>; + true -> + BinTerm + end end. -define(writeInt(Int), integer_to_binary(Term)). @@ -143,21 +168,51 @@ writeList([One | List], D, E, BinAcc) -> writeList(Other, D, E, BinAcc) -> <>. -writeTuple(Tuple, D, E, Index, TupleSize, BinAcc) -> +writeList(List, Depth, Width, Encoding, Strings) -> + case Strings andalso visableList(List) of + true -> + list_to_binary(List); + _ -> + writeList([], Depth, Widt, Encoding, Strings, SumLC, <<"[">>) + end. + +writeList([], Depth, Width, Encoding, Strings, SumLC, BinAcc) -> + <>; +writeList([One], Depth, Width, Encoding, Strings, SumLC, BinAcc) -> + TermBin = writeTerm(One, Depth, Width, Encoding, Strings), + TermBinBinSize = erlang:byte_size(TermBin), + NewSumLC = SumLC + TermBinBinSize, + case NewSumLC >= Width of + true -> + <>; + _ -> + <> + + end; +writeList([One | List], Depth, Width, Encoding, Strings, SumLC, BinAcc) -> if - D =:= 1 -> <>; + Depth =:= 1 -> <>; + true -> + writeList(List, Depth - 1, E, <>) + end; +writeList(Term, Depth, Width, Encoding, Strings, <<"[">>) -> + <>. + +writeTuple(Tuple, Depth, Width, CharsLimit, Encoding, Strings, Index, TupleSize, BinAcc) -> + if + Depth =:= 1 -> <>; true -> if Index < TupleSize -> - writeTuple(Tuple, D - 1, E, Index + 1, TupleSize, <>); + writeTuple(Tuple, Depth, Width, CharsLimit, Encoding, Strings, Index + 1, TupleSize, <>); Index == TupleSize -> - <>; + <>; true -> <> end end. -writeMap(Map, D, E, BinAcc) when is_integer(D) -> +writeMap(Map, D, E, BinAcc) -> if D =:= 1 -> <>; @@ -199,18 +254,42 @@ writeBinary(Bin, D, BinAcc) -> end end. -writeTerm(_Term, 0, _E) -> <<"...">>; -writeTerm(Term, _D, _E) when is_integer(Term) -> ?writeInt(Term); -writeTerm(Atom, _D, E) when is_atom(Atom) -> ?writeAtom(Atom, E); -writeTerm(Term, D, E) when is_list(Term) -> writeList(Term, D, E, <<"[">>); -writeTerm(Term, D, E) when is_map(Term) -> writeMap(Term, D, E, <<"#{">>); -writeTerm(Term, D, E) when is_tuple(Term) -> writeTuple(Term, D, E, 1, tuple_size(Term), <<"{">>); -writeTerm(Term, D, _E) when is_bitstring(Term) -> writeBinary(Term, D, <<"<<">>); -writeTerm(Term, _D, _E) when is_pid(Term) -> ?writePid(Term); -writeTerm(Term, _D, _E) when is_float(Term) -> ?writeFloat(Term); -writeTerm(Term, _D, _E) when is_port(Term) -> ?writePort(Term); -writeTerm(Term, _D, _E) when is_reference(Term) -> ?writeRef(Term); -writeTerm(Term, _D, _E) when is_function(Term) -> ?writeFun(Term). +writeBinary(Bin, Depth, Width, Encoding, Strings) -> + case Strings andalso visableBin(Bin) of + true -> + <<"<<", Bin/binary, ">>">>; + _ -> + writeBinary([], Depth, Width, Encoding, Strings, <<"<<">>) + end. + +writeTerm(_Term, Depth, _E) when Depth =< 0 -> <<"...">>; +writeTerm(Term, _Depth, _E) when is_integer(Term) -> ?writeInt(Term); +writeTerm(Term, _Depth, E) when is_atom(Term) -> ?writeAtom(Term, E); +writeTerm(Term, Depth, E) when is_list(Term) -> writeList(Term, Depth, E, <<"[">>); +writeTerm(Term, Depth, E) when is_map(Term) -> writeMap(Term, Depth, E, <<"#{">>); +writeTerm(Term, Depth, E) when is_tuple(Term) -> writeTuple(Term, Depth, E, 1, tuple_size(Term), <<"{">>); +writeTerm(Term, Depth, _E) when is_bitstring(Term) -> writeBinary(Term, Depth, <<"<<">>); +writeTerm(Term, _Depth, _E) when is_pid(Term) -> ?writePid(Term); +writeTerm(Term, _Depth, _E) when is_float(Term) -> ?writeFloat(Term); +writeTerm(Term, _Depth, _E) when is_port(Term) -> ?writePort(Term); +writeTerm(Term, _Depth, _E) when is_reference(Term) -> ?writeRef(Term); +writeTerm(Term, _Depth, _E) when is_function(Term) -> ?writeFun(Term). + +writeTerm(_Term, Depth, _Width, _Encoding, _Strings) when Depth =< 0 -> <<"...">>; +writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_integer(Term) -> ?writeInt(Term); +writeTerm(Term, _Depth, _Width, Encoding, _Strings) when is_atom(Term) -> ?writeAtom(Term, Encoding); +writeTerm(Term, Depth, Width, Encoding, Strings) when is_list(Term) -> writeList(Term, Depth, Width, Encoding, Strings); +writeTerm(Term, Depth, Width, Encoding, Strings) when is_map(Term) -> + writeMap(Term, Depth, Width, Encoding, Strings, <<"#{">>); +writeTerm(Term, Depth, Width, Encoding, Strings) when is_tuple(Term) -> + writeTuple(Term, Depth, Width, Encoding, Strings, 1, tuple_size(Term), <<"{">>); +writeTerm(Term, Depth, Width, Encoding, Strings) when is_bitstring(Term) -> + writeBinary(Term, Depth, Width, Encoding, Strings, <<"<<">>); +writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_pid(Term) -> ?writePid(Term); +writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_float(Term) -> ?writeFloat(Term); +writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_port(Term) -> ?writePort(Term); +writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_reference(Term) -> ?writeRef(Term); +writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_function(Term) -> ?writeFun(Term). %% ********************************************** eFmt end ************************************************************* @@ -223,19 +302,19 @@ fWrite(Format, Args) -> fWrite(Format, Args, Options) -> fBuild(fScan(Format, Args), Options). - +%% 格式 ~F.P.PadModC %% Parse all control sequences in the format string. -spec fScan(Format :: io:format(), Data :: [term()]) -> FormatList :: [char() | fmtSpec()]. - -%% 格式 ~F.P.PadModC fScan(Format, Args) -> if - is_atom(Format) -> - doCollect(atom_to_binary(Format, utf8), Args, []); + is_binary(Format) -> + doCollect(Format, Args, []); is_list(Format) -> doCollect(list_to_binary(Format), Args, []); + is_atom(Format) -> + doCollect(atom_to_binary(Format, utf8), Args, []); true -> - doCollect(Format, Args, []) + throw(bad_format) end. doCollect(FmtBinStr, Args, Acc) -> @@ -465,23 +544,19 @@ buildLimited([OneCA | Cs], NumOfPs, Count, MaxLen, I, Acc) -> #fmtSpec{ctlChar = CtlChar, args = Args, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding, strings = Strings} -> MaxChars = if MaxLen < 0 -> MaxLen; true -> MaxLen div Count end, IoListStr = ctlLimited(CtlChar, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, MaxChars, I), - NewNumOfPs = decrPc(CtlChar, NumOfPs), + NewNumOfPs = ?IIF(CtlChar == $p orelse CtlChar == $P, NumOfPs - 1, NumOfPs), NewCount = Count - 1, - MaxLen = ?IIF(MaxLen < 0, MaxLen, remainChars(MaxLen, charsLen(IoListStr))), + NewMaxLen = ?IIF(MaxLen < 0, MaxLen, remainChars(MaxLen, charsLen(IoListStr))), if NewNumOfPs > 0 -> - buildLimited(Cs, NewNumOfPs, NewCount, MaxLen, I, [IoListStr | Acc]); + buildLimited(Cs, NewNumOfPs, NewCount, NewMaxLen, I, [IoListStr | Acc]); true -> - buildLimited(Cs, NewNumOfPs, NewCount, MaxLen, I, [IoListStr | Acc]) + buildLimited(Cs, NewNumOfPs, NewCount, NewMaxLen, I, [IoListStr | Acc]) end; _ -> buildLimited(Cs, NumOfPs, Count, MaxLen, I + 1, [OneCA | Acc]) end. -decrPc($p, Pc) -> Pc - 1; -decrPc($P, Pc) -> Pc - 1; -decrPc(_, Pc) -> Pc. - %% (CtlChar, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, MaxChars, I) ctlLimited($s, Args, Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) -> case Encoding of @@ -499,13 +574,13 @@ ctlLimited($s, Args, Width, Adjust, Precision, PadChar, Encoding, _Strings, Char ctlLimited($w, Args, Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) -> Chars = write(Args, -1, Encoding, CharsLimit), term(Chars, Width, Adjust, Precision, PadChar); -ctlLimited($p, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I) -> - print(Args, -1, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I); +ctlLimited($p, Args, Width, _Adjust, _Precision, _PadChar, Encoding, Strings, CharsLimit, _I) -> + write(Args, -1, ?IIF(Width == none, ?LineCCnt, Width), CharsLimit, Encoding, Strings); ctlLimited($W, [Args, Depth], Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) -> Chars = write(Args, Depth, Encoding, CharsLimit), term(Chars, Width, Adjust, Precision, PadChar); -ctlLimited($P, [Args, Depth], Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I) -> - print(Args, Depth, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I). +ctlLimited($P, [Args, Depth], Width, _Adjust, _Precision, _PadChar, Encoding, Strings, CharsLimit, _I) -> + write(Args, Depth, ?IIF(Width == none, ?LineCCnt, Width), CharsLimit, Encoding, Strings). term(BinStrOrIoList, Width, Adjust, Precision, PadChar) -> if @@ -536,26 +611,22 @@ print(Term, Depth, Width, _Adjust, Precision, _PadChar, Encoding, Strings, Chars Width == none -> if Precision == none -> - eFmtPretty:print(Term, I + 1, 120, Depth, -1, CharsLimit, no_fun, Encoding, Strings); + print(Term, I + 1, ?LineCCnt, Depth, -1, CharsLimit, no_fun, Encoding, Strings); true -> - eFmtPretty:print(Term, Precision, 120, Depth, -1, CharsLimit, no_fun, Encoding, Strings) + print(Term, Precision, ?LineCCnt, Depth, -1, CharsLimit, no_fun, Encoding, Strings) end; true -> if Precision == none -> - eFmtPretty:print(Term, I + 1, Width, Depth, -1, CharsLimit, no_fun, Encoding, Strings); + print(Term, I + 1, Width, Depth, -1, CharsLimit, no_fun, Encoding, Strings); true -> - eFmtPretty:print(Term, Precision, Width, Depth, -1, CharsLimit, no_fun, Encoding, Strings) + print(Term, Precision, Width, Depth, -1, CharsLimit, no_fun, Encoding, Strings) end end. floatE(Float, Width, Adjust, Precision, PadChar) -> - case Precision of - none -> - NewPrecision = 6; - _ -> - NewPrecision = Precision - end, + NewPrecision = ?IIF(Precision == none, 6, Precision), + case Width of none -> float_to_binary(Float, [{scientific, NewPrecision}]); @@ -564,12 +635,8 @@ floatE(Float, Width, Adjust, Precision, PadChar) -> end. floatF(Float, Width, Adjust, Precision, PadChar) -> - case Precision of - none -> - NewPrecision = 6; - _ -> - NewPrecision = Precision - end, + NewPrecision = ?IIF(Precision == none, 6, Precision), + case Width of none -> float_to_binary(Float, [{decimals, NewPrecision}]); @@ -729,6 +796,11 @@ remainChars(T, E) -> end. %% ********************************************** eFmtFormat end ***************************************************** %% ********************************************** eFmtPretty start ***************************************************** +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%dfsdfsdfsdfsdff start + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%dfsdfsdfsdfsdff start + %% ********************************************** eFmtPretty end ***************************************************** %% ********************************************** utils start ********************************************************** toLowerStr(BinStr) -> diff --git a/src/eFmtPretty.erl b/src/eFmtPretty.erl index beee8d2..621b8fb 100644 --- a/src/eFmtPretty.erl +++ b/src/eFmtPretty.erl @@ -1,5 +1,7 @@ -module(eFmtPretty). +-include("eFmt.hrl"). + -export([ pPrint/1 , pPrint/2 @@ -24,7 +26,7 @@ -spec pPrint(term()) -> io_lib:chars(). pPrint(Term) -> - print(Term, 1, 120, -1, -1, -1, no_fun, latin1, true). + print(Term, 1, ?LineCCnt, -1, -1, -1, no_fun, latin1, true). %% print(Term, RecDefFun) -> [Chars] %% print(Term, Depth, RecDefFun) -> [Chars] @@ -55,7 +57,7 @@ pPrint(Term) -> pPrint(Term, Options) when is_list(Options) -> Col = get_option(column, Options, 1), - Ll = get_option(line_length, Options, 120), + Ll = get_option(line_length, Options, ?LineCCnt), D = get_option(depth, Options, -1), M = get_option(line_max_chars, Options, -1), T = get_option(chars_limit, Options, -1), @@ -69,7 +71,7 @@ pPrint(Term, RecDefFun) -> -spec pPrint(term(), depth(), rec_print_fun()) -> chars(). pPrint(Term, Depth, RecDefFun) -> - pPrint(Term, 1, 120, Depth, RecDefFun). + pPrint(Term, 1, ?LineCCnt, Depth, RecDefFun). -spec pPrint(term(), column(), line_length(), depth()) -> chars(). pPrint(Term, Col, Ll, D) -> @@ -100,13 +102,11 @@ print(_, _, _, _D, _M, 0, _RF, _Enc, _Str) -> "..."; print(Term, Col, Ll, D, M, T, RecDefFun, Enc, Str) when Col =< 0 -> %% ensure Col is at least 1 print(Term, 1, Ll, D, M, T, RecDefFun, Enc, Str); -print(Atom, _Col, _Ll, _D, _M, _T, _RF, Enc, _Str) when is_atom(Atom) -> - write_atom(Atom, Enc); print(Term, Col, Ll, D, M0, T, RecDefFun, Enc, Str) when is_tuple(Term); is_list(Term); is_map(Term); is_bitstring(Term) -> %% preprocess and compute total number of chars {_, Len, _Dots, _} = If = case T < 0 of - true -> print_length(Term, D, T, RecDefFun, Enc, Str); + true -> printTerm(Term, D, T, RecDefFun, Enc, Str); false -> pIntermediate(Term, D, T, RecDefFun, Enc, Str) end, %% use Len as CHAR_MAX if M0 = -1 @@ -126,7 +126,49 @@ print(Term, Col, Ll, D, M0, T, RecDefFun, Enc, Str) when is_tuple(Term); is_list end; print(Term, _Col, _Ll, _D, _M, _T, _RF, _Enc, _Str) -> %% atomic data types (bignums, atoms, ...) are never truncated - io_lib:write(Term). + eFmt:write(Term, _D, _Enc). + + +print(_, _, _, 0, _M, _T, _RF, _Enc, _Str) -> "..."; +print(_, _, _, _D, _M, 0, _RF, _Enc, _Str) -> "..."; +print(Term, Col, Ll, D, M, T, RecDefFun, Enc, Str) when Col =< 0 -> + %% ensure Col is at least 1 + print(Term, 1, Ll, D, M, T, RecDefFun, Enc, Str); +%% print(Term, Precision, Width, Depth, -1, CharsLimit, no_fun, Encoding, Strings) +print(Term, Precision, Width, Depth, M0_1, CharsLimit, RecDefFun, Encoding, Strings) when is_tuple(Term); is_list(Term); is_map(Term); is_bitstring(Term) -> + if + Depth == 0 orelse CharsLimit == 0 -> + <<"...">>; + true -> + NewPrecision == ?IIF(Precision =< 0, 1, Precision), + if + is_tuple(Term); is_list(Term); is_map(Term); is_bitstring(Term) -> + %% preprocess and compute total number of chars + {_, Len, _Dots, _} = If = + case CharsLimit < 0 of + true -> printTerm(Term, Depth, CharsLimit, RecDefFun, Encoding, Strings); + _ -> pIntermediate(Term, Depth, CharsLimit, RecDefFun, Encoding, Strings) + end, + %% use Len as CHAR_MAX if M0 = -1 + M = max_cs(M0_1, Len), + if + Width =:= 0 -> + pWrite(If); + Len < Width - NewPrecision, Len =< M -> + %% write the whole thing on a single line when there is room + pWrite(If); + true -> + %% compute the indentation TInd for tagged tuples and records + TInd = while_fail([-1, 4], + fun(I) -> cind(If, NewPrecision, Width, M, I, 0, 0) end, + 1), + pp(If, NewPrecision, Width, M, TInd, indent(NewPrecision), 0, 0) + end; + true -> + eFmt:write(Term, Depth, Encoding, CharsLimit) + end + end. + %%% %%% Local functions @@ -425,7 +467,7 @@ write_tail(E, S) -> pIntermediate(Term, Depth, CharsLimit, RF, Enc, Str) -> D0 = 1, - If = print_length(Term, D0, CharsLimit, RF, Enc, Str), + If = printTerm(Term, D0, CharsLimit, RF, Enc, Str), case If of {_, Len, Dots, _} when Dots =:= 0; Len > CharsLimit; Depth =:= 1 -> If; @@ -476,24 +518,10 @@ search_depth(Lower, Upper, Term, T, Dl, Du, RF, Enc, Str) -> search_depth(If, Upper, Term, T, D1, Du, RF, Enc, Str) end. -%% The depth (D) is used for extracting and counting the characters to -%% print. The structure is kept so that the returned intermediate -%% format can be formatted. The separators (list, tuple, record, map) are -%% counted but need to be added later. - -%% D =/= 0 -print_length([], _D, _T, _RF, _Enc, _Str) -> - {"[]", 2, 0, no_more}; -print_length({}, _D, _T, _RF, _Enc, _Str) -> - {"{}", 2, 0, no_more}; -print_length(#{} = M, _D, _T, _RF, _Enc, _Str) when map_size(M) =:= 0 -> - {"#{}", 3, 0, no_more}; -print_length(Atom, _D, _T, _RF, Enc, _Str) when is_atom(Atom) -> - S = write_atom(Atom, Enc), - {S, io_lib:chars_length(S), 0, no_more}; -print_length(List, D, T, RF, Enc, Str) when is_list(List) -> + +printTerm(List, D, T, RF, Enc, Str) when is_list(List) -> %% only flat lists are "printable" - case Str andalso printable_list(List, D, T, Enc) of + case Str andalso visualList(List, D, T, Enc) of true -> %% print as string, escaping double-quotes in the list S = write_string(List, Enc), @@ -515,27 +543,11 @@ print_length(List, D, T, RF, Enc, Str) when is_list(List) -> If end end; -print_length(Fun, _D, _T, _RF, _Enc, _Str) when is_function(Fun) -> - S = io_lib:write(Fun), - {S, iolist_size(S), 0, no_more}; -print_length(R, D, T, RF, Enc, Str) when is_atom(element(1, R)), - is_function(RF) -> - case RF(element(1, R), tuple_size(R) - 1) of - no -> - print_length_tuple(R, D, T, RF, Enc, Str); - RDefs -> - print_length_record(R, D, T, RF, RDefs, Enc, Str) - end; -print_length(Tuple, D, T, RF, Enc, Str) when is_tuple(Tuple) -> +printTerm(Tuple, D, T, RF, Enc, Str) when is_tuple(Tuple) -> print_length_tuple(Tuple, D, T, RF, Enc, Str); -print_length(Map, D, T, RF, Enc, Str) when is_map(Map) -> +printTerm(Map, D, T, RF, Enc, Str) when is_map(Map) -> print_length_map(Map, D, T, RF, Enc, Str); -print_length(<<>>, _D, _T, _RF, _Enc, _Str) -> - {"<<>>", 4, 0, no_more}; -print_length(<<_/bitstring>> = Bin, 1, _T, RF, Enc, Str) -> - More = fun(T1, Dd) -> ?FUNCTION_NAME(Bin, 1 + Dd, T1, RF, Enc, Str) end, - {"<<...>>", 7, 3, More}; -print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) -> +printTerm(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) -> D1 = D - 1, case Str andalso (bit_size(Bin) rem 8) =:= 0 andalso printable_bin0(Bin, D1, tsub(T, 6), Enc) of @@ -567,11 +579,7 @@ print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) -> end, {{bin, S}, iolist_size(S), 3, More} end - end; -print_length(Term, _D, _T, _RF, _Enc, _Str) -> - S = io_lib:write(Term), - %% S can contain unicode, so iolist_size(S) cannot be used here - {S, io_lib:chars_length(S), 0, no_more}. + end. print_length_map(Map, 1, _T, RF, Enc, Str) -> More = fun(T1, Dd) -> ?FUNCTION_NAME(Map, 1 + Dd, T1, RF, Enc, Str) end, @@ -597,9 +605,9 @@ print_length_map_pairs({K, V, Iter}, D, D0, T, RF, Enc, Str) -> print_length_map_pairs(Next, D - 1, D0, tsub(T, Len1 + 1), RF, Enc, Str)]. print_length_map_pair(K, V, D, T, RF, Enc, Str) -> - {_, KL, KD, _} = P1 = print_length(K, D, T, RF, Enc, Str), + {_, KL, KD, _} = P1 = printTerm(K, D, T, RF, Enc, Str), KL1 = KL + 4, - {_, VL, VD, _} = P2 = print_length(V, D, tsub(T, KL1), RF, Enc, Str), + {_, VL, VD, _} = P2 = printTerm(V, D, tsub(T, KL1), RF, Enc, Str), {{map_pair, P1, P2}, KL1 + VL, KD + VD, no_more}. print_length_tuple(Tuple, 1, _T, RF, Enc, Str) -> @@ -620,7 +628,7 @@ print_length_tuple1(Tuple, I, D, T, RF, Enc, Str) when D =:= 1; T =:= 0 -> print_length_tuple1(Tuple, I, D, T, RF, Enc, Str) -> E = element(I, Tuple), T1 = tsub(T, 1), - {_, Len1, _, _} = Elem1 = print_length(E, D - 1, T1, RF, Enc, Str), + {_, Len1, _, _} = Elem1 = printTerm(E, D - 1, T1, RF, Enc, Str), T2 = tsub(T1, Len1), [Elem1 | print_length_tuple1(Tuple, I + 1, D - 1, T2, RF, Enc, Str)]. @@ -659,7 +667,7 @@ print_length_field(Def, D, T, E, RF, Enc, Str) -> Name = write_atom(Def, Enc), NameL = io_lib:chars_length(Name) + 3, {_, Len, Dots, _} = - Field = print_length(E, D, tsub(T, NameL), RF, Enc, Str), + Field = printTerm(E, D, tsub(T, NameL), RF, Enc, Str), {{field, Name, NameL, Field}, NameL + Len, Dots, no_more}. print_length_list(List, D, T, RF, Enc, Str) -> @@ -673,10 +681,10 @@ print_length_list1(Term, D, T, RF, Enc, Str) when D =:= 1; T =:= 0 -> More = fun(T1, Dd) -> ?FUNCTION_NAME(Term, D + Dd, T1, RF, Enc, Str) end, {dots, 3, 3, More}; print_length_list1([E | Es], D, T, RF, Enc, Str) -> - {_, Len1, _, _} = Elem1 = print_length(E, D - 1, tsub(T, 1), RF, Enc, Str), + {_, Len1, _, _} = Elem1 = printTerm(E, D - 1, tsub(T, 1), RF, Enc, Str), [Elem1 | print_length_list1(Es, D - 1, tsub(T, Len1 + 1), RF, Enc, Str)]; print_length_list1(E, D, T, RF, Enc, Str) -> - print_length(E, D - 1, T, RF, Enc, Str). + printTerm(E, D - 1, T, RF, Enc, Str). list_length([], Acc, DotsAcc) -> {Acc, DotsAcc}; @@ -696,11 +704,11 @@ list_length_tail({_, Len, Dots, _}, Acc, DotsAcc) -> -define(CHARS, 4). %% only flat lists are "printable" -printable_list(_L, 1, _T, _Enc) -> +visualList(_L, 1, _T, _Enc) -> false; -printable_list(L, _D, T, latin1) when T < 0 -> +visualList(L, _D, T, latin1) when T < 0 -> io_lib:printable_latin1_list(L); -printable_list(L, _D, T, latin1) when T >= 0 -> +visualList(L, _D, T, latin1) when T >= 0 -> N = tsub(T, 2), case printable_latin1_list(L, N) of all -> @@ -711,7 +719,7 @@ printable_list(L, _D, T, latin1) when T >= 0 -> _NC -> false end; -printable_list(L, _D, T, _Unicode) when T >= 0 -> +visualList(L, _D, T, _Unicode) when T >= 0 -> N = tsub(T, 2), %% Be careful not to traverse more of L than necessary. try string:slice(L, 0, N) of @@ -732,7 +740,7 @@ printable_list(L, _D, T, _Unicode) when T >= 0 -> end catch _:_ -> false end; -printable_list(L, _D, T, _Uni) when T < 0 -> +visualList(L, _D, T, _Uni) when T < 0 -> io_lib:printable_list(L). is_flat(_L, 0) -> diff --git a/src/test/recon-2.5.1/recon.erl b/src/test/recon-2.5.1/recon.erl index c085cb0..d5a7045 100644 --- a/src/test/recon-2.5.1/recon.erl +++ b/src/test/recon-2.5.1/recon.erl @@ -79,35 +79,35 @@ %%% @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]). + 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]). + 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]). + 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]). + named_rpc/1, named_rpc/2, named_rpc/3]). %%%%%%%%%%%%% %%% TYPES %%% %%%%%%%%%%%%% -type proc_attrs() :: {pid(), - Attr::_, - [Name::atom() - |{current_function, mfa()} - |{initial_call, mfa()}, ...]}. + Attr :: _, + [Name :: atom() + |{current_function, mfa()} + |{initial_call, mfa()}, ...]}. -type inet_attrs() :: {port(), - Attr::_, - [{atom(), term()}]}. + Attr :: _, + [{atom(), term()}]}. -type pid_term() :: pid() | atom() | string() - | {global, term()} | {via, module(), term()} - | {non_neg_integer(), non_neg_integer(), non_neg_integer()}. +| {global, term()} | {via, module(), term()} +| {non_neg_integer(), non_neg_integer(), non_neg_integer()}. -type info_type() :: meta | signals | location | memory_used | work. @@ -115,11 +115,11 @@ -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. +| 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(). +| info_memory_key() | info_work_key(). -type port_term() :: port() | string() | atom() | pos_integer(). @@ -132,16 +132,16 @@ -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(). +| 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]). + 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]). + 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 %%% @@ -151,16 +151,16 @@ %% @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)). +-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). +-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 @@ -174,11 +174,11 @@ info(A,B,C, Key) -> info(recon_lib:triple_to_pid(A,B,C), Key). %% 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(). +-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]]. + 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 @@ -198,66 +198,66 @@ info(PidTerm) -> %% 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()}. +; (pid_term(), [atom()]) -> [{atom(), term()}] +; (pid_term(), atom()) -> {atom(), term()}. info(PidTerm, meta) -> - info_type(PidTerm, meta, [registered_name, dictionary, group_leader, - status]); + info_type(PidTerm, meta, [registered_name, dictionary, group_leader, + status]); info(PidTerm, signals) -> - info_type(PidTerm, signals, [links, monitors, monitored_by, trap_exit]); + info_type(PidTerm, signals, [links, monitors, monitored_by, trap_exit]); info(PidTerm, location) -> - info_type(PidTerm, location, [initial_call, current_stacktrace]); + 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_type(PidTerm, memory_used, [memory, message_queue_len, heap_size, + total_heap_size, garbage_collection]); info(PidTerm, work) -> - info_type(PidTerm, work, [reductions]); + info_type(PidTerm, work, [reductions]); info(PidTerm, Keys) -> - proc_info(recon_lib:term_to_pid(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(), [{info_key(), term()}]}. info_type(PidTerm, Type, Keys) -> - Pid = recon_lib:term_to_pid(PidTerm), - {Type, proc_info(Pid, 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)}; + {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); + 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. + 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)]. +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)]. + []; +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(). + AttributeName :: atom(), + Num :: non_neg_integer(). proc_count(AttrName, Num) -> - recon_lib:sublist_top_n_attrs(recon_lib:proc_attrs(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. @@ -285,13 +285,13 @@ proc_count(AttrName, Num) -> %% 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(). + 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). + 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 @@ -308,24 +308,24 @@ proc_window(AttrName, Num, Time) -> %% 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]. + 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(). + 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). + 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 @@ -346,29 +346,29 @@ node_stats_print(N, Interval) -> %% %% 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(). + 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). + %% 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()}]}. + 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, [])). + 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'. @@ -386,71 +386,71 @@ node_stats_list(N, Interval) -> %% 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()}]}. + 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}]}, + 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. + {{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 %%% @@ -462,22 +462,22 @@ 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(). +-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. + 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 %%% @@ -488,14 +488,14 @@ 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), - rpc:multicall(Nodes, code, load_binary, [Mod, File, Bin]); -remote_load(Nodes=[_|_], Modules) when is_list(Modules) -> - [remote_load(Nodes, Mod) || Mod <- Modules]; + Nodes :: [node(), ...] | node(). +remote_load(Nodes = [_ | _], Mod) when is_atom(Mod) -> + {Mod, Bin, File} = code:get_object_code(Mod), + rpc: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). + 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 @@ -505,9 +505,9 @@ remote_load(Node, Mod) -> %% @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)). + Path = code:which(Module), + {ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(Path, [abstract_code]), + erl_prettypr:format(erl_syntax:form_list(AC)). %%% Ports Info %%% @@ -532,13 +532,13 @@ 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()}]. +-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)]) - ). + 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. @@ -549,11 +549,11 @@ port_types() -> %% 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(). + 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). + 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. @@ -568,14 +568,14 @@ inet_count(Attr, Num) -> %% 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(). + 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). + 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 @@ -591,11 +591,11 @@ inet_window(Attr, Num, Time) when is_atom(Attr) -> %% 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_key(), term()}]}, ...]. port_info(PortTerm) -> - Port = recon_lib:term_to_port(PortTerm), - [port_info(Port, Type) || Type <- [meta, signals, io, memory_used, - specific]]. + 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 @@ -608,106 +608,106 @@ port_info(PortTerm) -> %% 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_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; + {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_type(PortTerm, signals, [connected, links, monitors]); port_info(PortTerm, io) -> - port_info_type(PortTerm, io, [input, output]); + port_info_type(PortTerm, io, [input, output]); port_info(PortTerm, memory_used) -> - port_info_type(PortTerm, memory_used, [memory, queue_size]); + 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 = 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 = 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). + 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]}. + 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::_]}. +-spec rpc(fun(() -> term())) -> {[Success :: _], [Fail :: _]}. rpc(Fun) -> - rpc([node()|nodes()], Fun). + rpc([node() | nodes()], Fun). %% @doc Shorthand for `rpc(Nodes, Fun, infinity)'. --spec rpc(node()|[node(),...], fun(() -> term())) -> {[Success::_],[Fail::_]}. +-spec rpc(node()|[node(), ...], fun(() -> term())) -> {[Success :: _], [Fail :: _]}. rpc(Nodes, Fun) -> - rpc(Nodes, Fun, infinity). + 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) -> - rpc:multicall(Nodes, erlang, apply, [Fun,[]], Timeout); +-spec rpc(node()|[node(), ...], fun(() -> term()), timeout()) -> {[Success :: _], [Fail :: _]}. +rpc(Nodes = [_ | _], Fun, Timeout) when is_function(Fun, 0) -> + rpc:multicall(Nodes, erlang, apply, [Fun, []], Timeout); rpc(Node, Fun, Timeout) when is_atom(Node) -> - rpc([Node], Fun, Timeout). + rpc([Node], Fun, Timeout). %% @doc Shorthand for `named_rpc([node()|nodes()], Fun)'. --spec named_rpc(fun(() -> term())) -> {[Success::_],[Fail::_]}. +-spec named_rpc(fun(() -> term())) -> {[Success :: _], [Fail :: _]}. named_rpc(Fun) -> - named_rpc([node()|nodes()], Fun). + named_rpc([node() | nodes()], Fun). %% @doc Shorthand for `named_rpc(Nodes, Fun, infinity)'. --spec named_rpc(node()|[node(),...], fun(() -> term())) -> {[Success::_],[Fail::_]}. +-spec named_rpc(node()|[node(), ...], fun(() -> term())) -> {[Success :: _], [Fail :: _]}. named_rpc(Nodes, Fun) -> - named_rpc(Nodes, Fun, infinity). + 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) -> - rpc:multicall(Nodes, erlang, apply, [fun() -> {node(),Fun()} end,[]], Timeout); +-spec named_rpc(node()|[node(), ...], fun(() -> term()), timeout()) -> {[Success :: _], [Fail :: _]}. +named_rpc(Nodes = [_ | _], Fun, Timeout) when is_function(Fun, 0) -> + rpc:multicall(Nodes, erlang, apply, [fun() -> {node(), Fun()} end, []], Timeout); named_rpc(Node, Fun, Timeout) when is_atom(Node) -> - named_rpc([Node], Fun, Timeout). + 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 index 46d2bd7..fb62a5e 100644 --- a/src/test/recon-2.5.1/recon_alloc.erl +++ b/src/test/recon-2.5.1/recon_alloc.erl @@ -115,19 +115,19 @@ %%% -module(recon_alloc). -define(UTIL_ALLOCATORS, [temp_alloc, - eheap_alloc, - binary_alloc, - ets_alloc, - driver_alloc, - sl_alloc, - ll_alloc, - fix_alloc, - std_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. +| 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}. @@ -137,18 +137,18 @@ -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]). + average_block_sizes/1, sbcs_to_mbcs/1, allocators/0, + allocators/1]). %% Snapshot handling --type memory() :: [{atom(),atom()}]. --type snapshot() :: {memory(),[allocdata(term())]}. +-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]). +-export([snapshot/0, snapshot_clear/0, + snapshot_print/0, snapshot_get/0, + snapshot_save/1, snapshot_load/1]). %% Unit handling -export([set_unit/1]). @@ -160,9 +160,9 @@ %% @doc Equivalent to `memory(Key, current)'. -spec memory(used | allocated | unused) -> pos_integer() - ; (usage) -> number() - ; (allocated_types | allocated_instances) -> - [{allocator(), 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 @@ -201,31 +201,31 @@ memory(Key) -> memory(Key, current). %% 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). +; (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. @@ -244,16 +244,16 @@ memory(usage,Keyword) -> %% 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))]. + 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 @@ -277,20 +277,20 @@ fragmentation(Keyword) -> %% %% 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(). +-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))]. + 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 @@ -307,24 +307,24 @@ cache_hit_rates() -> %% %% 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(). +-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) -> + 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), + 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)))). + 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 @@ -352,79 +352,79 @@ average_block_sizes(Keyword) -> %% 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))]. + 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]. + 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)]. + %% {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)]; + %% 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)]. + [H | format_blocks(Alloc, Key, T)]. %% @doc returns a dump of all allocator settings and values modified %% depending on the argument. @@ -435,70 +435,70 @@ format_blocks(Alloc, Key, [H | T]) -> %% -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(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([],[]) -> - []. + [{{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]); + {value, {memkind, MemKindVs}, OVs} = lists:keytake(memkind, 1, Vs), + lists:sort([{memkind, lists:sort(MemKindVs)} | OVs]); sort_values(_Type, Vs) -> - lists:sort(Vs). + lists:sort(Vs). %%%%%%%%%%%%%%%%%%%%%%%%% %%% Snapshot handling %%% @@ -510,7 +510,7 @@ sort_values(_Type, Vs) -> %% To unsert the snapshot, see {@link snapshot_clear/1}. -spec snapshot() -> snapshot() | undefined. snapshot() -> - put(recon_alloc_snapshot, snapshot_int()). + 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. @@ -518,37 +518,37 @@ snapshot() -> %% Maybe we should use erlang:delete(Key) here instead? -spec snapshot_clear() -> snapshot() | undefined. snapshot_clear() -> - put(recon_alloc_snapshot, undefined). + 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()]). + 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). + 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(). + 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. + 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 @@ -577,26 +577,26 @@ snapshot_save(Filename) -> %% 18411064''' %% -spec snapshot_load(Filename) -> snapshot() | undefined when - Filename :: file:name(). + 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). + {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 %%% @@ -615,62 +615,62 @@ snapshot_load(Filename) -> %% -spec set_unit(byte | kilobyte | megabyte | gigabyte) -> ok. set_unit(byte) -> - put(recon_alloc_unit,undefined); + put(recon_alloc_unit, undefined); set_unit(kilobyte) -> - put(recon_alloc_unit,1024); + put(recon_alloc_unit, 1024); set_unit(megabyte) -> - put(recon_alloc_unit,1024*1024); + 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) -> - []. + 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 %%% @@ -680,99 +680,99 @@ conv_alloc([],_Factor) -> %% 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}]}. + {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(0, 0) -> 1.00; +usage(0.0, 0.0) -> 1.00; %usage(N,0) -> ???; -usage(Block,Carrier) -> Block/Carrier. +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. + []; +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)]. +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. + 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); + 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)); + 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)). + 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()}. + {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. + 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. + {_Mem, Allocs} = snapshot_get_int(), + Allocs. alloc(Type) -> - [{{T,Instance},Props} || {{T,Instance},Props} <- alloc(), - lists:member(T,Type)]. + [{{T, Instance}, Props} || {{T, Instance}, Props} <- alloc(), + lists:member(T, Type)]. %% Get only alloc_util allocs util_alloc() -> - alloc(?UTIL_ALLOCATORS). + alloc(?UTIL_ALLOCATORS). key2pos(current) -> - ?CURRENT_POS; + ?CURRENT_POS; key2pos(max) -> - ?MAX_POS. + ?MAX_POS. diff --git a/src/test/recon-2.5.1/recon_lib.erl b/src/test/recon-2.5.1/recon_lib.erl index 0219a34..8bb90b0 100644 --- a/src/test/recon-2.5.1/recon_lib.erl +++ b/src/test/recon-2.5.1/recon_lib.erl @@ -6,14 +6,14 @@ %%% @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]). + 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]). @@ -22,66 +22,66 @@ %% @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(). +-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)]. + 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()}. +-spec sample(Ms :: non_neg_integer(), fun(() -> term())) -> + {First :: term(), Second :: term()}. sample(Delay, Fun) -> - First = Fun(), - timer:sleep(Delay), - Second = Fun(), - {First, Second}. + 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()}]. +-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). + 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()}]. +-spec port_list(Attr :: atom()) -> [{port(), term()}]. port_list(Attr) -> - [{Port,Val} || Port <- erlang:ports(), - {_, Val} <- [erlang:port_info(Port, 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()]. +-spec port_list(Attr :: atom(), term()) -> [port()]. port_list(Attr, Val) -> - [Port || Port <- erlang:ports(), - {Attr, Val} =:= erlang:port_info(Port, Attr)]. + [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)] - ]. + 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. @@ -90,196 +90,196 @@ proc_attrs(AttrName) -> %% 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; + 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. + 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)]]. + 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'. +| {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. + 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(). +-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) ++ ">"). + 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(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). +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("#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). + %% 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()). + 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)]. + {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(). + 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; + 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). + 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(). + 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; + 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)) - ). + 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(). + when Attrs :: recon:proc_attrs() | recon:inet_attrs(). sublist_top_n_attrs(_, 0) -> - %% matching lists:sublist/2 behaviour - []; + %% matching lists:sublist/2 behaviour + []; sublist_top_n_attrs(List, Len) -> - pheap_fill(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). + lists:foldl(fun({_, Mem, _}, Tot) -> Mem + Tot end, 0, Bins). %%%%%%%%%%%%%%% %%% PRIVATE %%% %%%%%%%%%%%%%%% pheap_fill(List, 0, Heap) -> - pheap_full(List, 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_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(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]). +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]. +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([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)). +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 index f80e587..2235846 100644 --- a/src/test/recon-2.5.1/recon_map.erl +++ b/src/test/recon-2.5.1/recon_map.erl @@ -23,15 +23,15 @@ %% @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. + 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. + 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 @@ -44,48 +44,48 @@ clear() -> %% @end -spec limit(map_label(), pattern(), limit()) -> ok | {error, any()}. limit(Label, #{} = Pattern, Limit) when is_atom(Label) -> - store_pattern(Label, Pattern, Limit); + store_pattern(Label, Pattern, Limit); limit(Label, Pattern, Limit) when is_atom(Label), is_function(Pattern) -> - store_pattern(Label, Pattern, Limit). + 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())). + 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). + 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. + 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; + io:format("~n"), + ok; list([{Label, Pattern, Limit} | Rest]) -> - io:format("~p: ~p -> ~p~n", [Label, Pattern, Limit]), - list(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. @@ -96,48 +96,48 @@ list([{Label, Pattern, Limit} | Rest]) -> %% -spec process_map(map()) -> map() | {atom(), map()}. process_map(M) -> - process_map(M, ets:tab2list(patterns_table_name())). + process_map(M, ets:tab2list(patterns_table_name())). process_map(M, []) -> - 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. + 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); + Pattern(M); map_matches(_, []) -> - true; + 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. + 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; + M; apply_map_limits(all, _) -> - #{}; + #{}; apply_map_limits(Fields, M) -> - maps:with(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. + ensure_table_exists(), + ets:insert(patterns_table_name(), {Label, prepare_pattern(Pattern), prepare_limit(Limit)}), + ok. prepare_limit(all) -> all; prepare_limit(none) -> none; @@ -150,59 +150,59 @@ 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. + 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. + 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. + 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. + 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 index 3689df1..f48615e 100644 --- a/src/test/recon-2.5.1/recon_rec.erl +++ b/src/test/recon-2.5.1/recon_rec.erl @@ -29,8 +29,8 @@ -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()}. +| {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 @@ -41,26 +41,26 @@ %% @end -spec import(module() | [module()]) -> import_result() | [import_result()]. import(Modules) when is_list(Modules) -> - lists:foldl(fun import/2, [], Modules); + lists:foldl(fun import/2, [], Modules); import(Module) -> - 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. + 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())). + 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. + 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, @@ -68,21 +68,21 @@ clear() -> %% 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()). + 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). + 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 @@ -90,20 +90,20 @@ get_list() -> %% @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. + 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). + ensure_table_exists(), + First = element(1, Tuple), + format_tuple(First, Tuple). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% PRIVATE @@ -111,93 +111,93 @@ format_tuple(Tuple) -> make_list_entry({{Name, _}, Fields, Module, Limits}) -> - FmtLimit = case Limits of - [] -> none; - Other -> Other - end, - {Module, Name, Fields, FmtLimit}. + 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)). + 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]. + {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). + 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}). + 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. + 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}. + {{Name, length(Fields)}, field_names(Fields), Module, none}. rem_for_module({_, _, Module, _} = Rec, Module) -> - ets:delete_object(records_table_name(), Rec); + ets:delete_object(records_table_name(), Rec); rem_for_module(_, _) -> - ok. + ok. ets_keeper() -> - receive - stop -> ok; - _ -> ets_keeper() - end. + receive + stop -> ok; + _ -> ets_keeper() + end. field_names(Fields) -> - lists:map(fun field_name/1, Fields). + lists:map(fun field_name/1, Fields). field_name({record_field, _, {atom, _, Name}}) -> Name; field_name({record_field, _, {atom, _, Name}, _Default}) -> Name; @@ -208,72 +208,72 @@ field_name({typed_record_field, Field, _Type}) -> field_name(Field). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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; + 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(Tuple). format_default(Val) -> - io_lib:format("~p", [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. + 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)]. + %% 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, '...'}]; + [{Field, proplists:get_value(Field, List)}, {more, '...'}]; apply_limits(List, Limits) -> - lists:filter(fun({K, _}) -> lists:member(K, Limits) end, List) ++ [{more, '...'}]. + 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. + 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. + 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). + lists:join(Sep, List). -else. -spec join(string(), [string()]) -> string(). join(Sep, List) -> - string:join(List, Sep). + string:join(List, Sep). -endif. diff --git a/src/test/recon-2.5.1/recon_trace.erl b/src/test/recon-2.5.1/recon_trace.erl index 2b8d005..a0bc574 100644 --- a/src/test/recon-2.5.1/recon_trace.erl +++ b/src/test/recon-2.5.1/recon_trace.erl @@ -187,34 +187,34 @@ %% 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 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(). +-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]). + max_traces/0, max_rate/0]). %%%%%%%%%%%%%% %%% PUBLIC %%% @@ -223,19 +223,19 @@ %% @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. + 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(). +-spec calls(tspec() | [tspec(), ...], max()) -> num_matches(). calls({Mod, Fun, Args}, Max) -> - calls([{Mod,Fun,Args}], Max, []); -calls(TSpecs = [_|_], Max) -> - calls(TSpecs, 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. @@ -332,31 +332,31 @@ calls(TSpecs = [_|_], Max) -> %% 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(). +-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). + 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); + exit(normal); count_tracer(N) -> - receive - Msg -> - recon_trace_formatter ! Msg, - count_tracer(N-1) - end. + 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 @@ -367,35 +367,35 @@ count_tracer(N) -> 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. + 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). + 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. + 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. %%%%%%%%%%%%%%%%%%%%%%% @@ -405,28 +405,28 @@ formatter(Tracer, IOServer, FormatterFun) -> %% 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. + 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). + {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). %%%%%%%%%%%%%%%%%% @@ -434,251 +434,251 @@ trace_calls(TSpecs, Pid, Opts) -> %%%%%%%%%%%%%%%%%% 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}. + 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(), ...]. +-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 = [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)]. + %% 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)); + 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, [{'_', [], [{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. + 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. + 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()). + 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). + {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. + 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}; + {_, {H, M, Secs}} = calendar:now_to_local_time(Stamp), + Seconds = Secs rem 60 + (Micro / 1000000), + {H, M, Seconds}; to_hms(_) -> - {0,0,0}. + {0, 0, 0}. format_args(Arity) when is_integer(Arity) -> - [$/, integer_to_list(Arity)]; + [$/, integer_to_list(Arity)]; format_args(Args) when is_list(Args) -> - [$(, join(", ", [format_trace_output(Arg) || Arg <- 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(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(Recs, recon_map:is_active(), Args). format_trace_output(true, _, Args) when is_tuple(Args) -> - recon_rec:format_tuple(Args); + recon_rec:format_tuple(Args); format_trace_output(false, true, Args) when is_tuple(Args) -> - format_tuple(false, true, 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; + 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]), - "}"]; + {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]), - "}"]; + ItemList = maps:to_list(Args), + ["#{", + join(", ", [format_kv(Recs, false, Key, Val) || {Key, Val} <- ItemList]), + "}"]; format_trace_output(_, _, Args) -> - io_lib:format("~p", [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_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, 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_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_(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), $]]. + "]"; +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), $]]. %%%%%%%%%%%%%%% @@ -686,48 +686,48 @@ format_maybe_improper_list_(Recs, Maps, [H|T]) when not is_list(T) -> %%%%%%%%%%%%%%% maybe_kill(Name) -> - case whereis(Name) of - undefined -> - ok; - Pid -> - unlink(Pid), - exit(Pid, kill), - wait_for_death(Pid, Name) - end. + 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. + 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. + 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). + lists:join(Sep, List). -else. -spec join(string(), [string()]) -> string(). join(Sep, List) -> - string:join(List, Sep). + string:join(List, Sep). -endif. diff --git a/src/test/recon-2.5.1/test.erl b/src/test/recon-2.5.1/test.erl index 6d69c01..112f948 100644 --- a/src/test/recon-2.5.1/test.erl +++ b/src/test/recon-2.5.1/test.erl @@ -43,7 +43,7 @@ memInfoPrint(CurModule, CurLine, Threshold) -> end. get_test() -> - #{'$map_tab' => tb_client,black_hole => 6,bomb_drill => 7,cat_coin => 680, + #{'$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 => @@ -260,38 +260,38 @@ get_test() -> <<"{\"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, + 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_comic_strips => #{5101 => {0, 2}}, act_icecream_truck => #{4001 => {0, - #{-1047817473 => 0,-884040730 => 0,132422520 => 40}, + #{-1047817473 => 0, -884040730 => 0, 132422520 => 40}, #{1 => 0}}}, - act_milk => #{2001 => {3,1}}, + 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}, + #{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]}, + cat_room => {2001, [200104, 200103, 200102, 200101]}, cats => <<"p">>, - chapter => {202,2,"ÔÓÒÑÐÏÎÍÌËÊÉ"}, - feed => {0,0,0,0,1}, + chapter => {202, 2, "ÔÓÒÑÐÏÎÍÌËÊÉ"}, + feed => {0, 0, 0, 0, 1}, guide_awards => [[]], icons => <<>>, - limit_room => {[],[]}, - milk_bottle => 1,ml => [],special_back => #{},spoint => 0, - store => <<>>,styles => [] + 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, + 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() ->