Current section
Files
Jump to
Current section
Files
src/xprof_ms.erl
-module(xprof_ms).
-compile(export_all).
fun2ms(Str) ->
try
case tokens(Str) of
{mfa, _M, _F, _Arity} = MFA ->
MFA;
{clauses, M, F, Tokens} ->
Clauses = parse(Tokens),
MS = ms(Clauses),
{ms, M, F, fix_ms(MS)}
end
catch throw:Error ->
Error
end.
tokens(Str) ->
case erl_scan:string(Str, {1,1}) of
{error, {_Loc, Mod, Err}, Loc} ->
err(Loc, Mod, Err);
{ok, [{atom, _, M}, {':', _},
{atom, _, F}, {'/', _},
{integer, _, A}], _EndLoc} ->
{mfa, M, F, A};
{ok, [{atom, _, M}, {':', _},
{atom, _, F}|Tokens], _EndLoc} when Tokens =/= [] ->
{clauses, M, F, [{'fun', 0}|ensure_end(Tokens)]};
{ok, Tokens, _EndLoc} ->
err("expression is not an xprof match-spec fun ~w", [Tokens])
end.
%% @doc Ensure the fun is properly closed with "end."
ensure_end(Tokens) ->
case lists:reverse(Tokens) of
[{dot, _}, {'end', _}| _] -> Tokens;
[{dot, Loc}|T] -> lists:reverse(T, [{'end', Loc}, {dot, Loc}]);
[Last|_] = R ->
Loc = element(2, Last),
lists:reverse(R, [{'end', Loc}, {dot, Loc}])
end.
parse(Tokens) ->
case erl_parse:parse_exprs(Tokens) of
{error, {Loc, Mod, Err}} ->
err(Loc, Mod, Err);
{ok, [{'fun', _Loc, {clauses, Clauses}}]} ->
Clauses;
{ok, _} ->
err("expression is not an xprof match-spec fun")
end.
ms(Clauses) ->
case ms_transform:transform_from_shell(
dbg, Clauses, _ImportList = []) of
{error,[{_,[{Loc,Mod,Code}|_]}|_],_} ->
err(Loc, Mod, Code);
MS ->
MS
end.
%% @doc Ensure that the match-spec does not create traces that have different
%% format than what xprof_trace_handler anticipates (ie. {message, _} directives
%% might have to be modified)
%% - The special case {message, false} is allowed (disables sending of trace
%% messages ('call' and 'return_to') for this function call, just like if the
%% match specification had not matched).
%% - The special case {message, true} must be avoided (sets the default
%% behavior, ie. trace message is sent with no extra information). These calls
%% are replaced with our default message ({message, arity} or {message, '$_'})
%% - Other values for messages are allowed but later overriden by placing
%% {message, arity} at the end of the match-spec body in case argument capturing
%% is off
%% - For the general case when the match-spec body does not contain any message
%% directive a default message ({message, arity} or {message, '$_'}) is inserted
%% as the first action of the body as well as enabling return_trace
fix_ms(MS) ->
{traverse_ms(MS, _CaptureOff = false),
traverse_ms(MS, _CaptureOn = true)}.
traverse_ms(MS, Capture) ->
DefaultMsg =
case Capture of
false -> arity;
true -> '$_'
end,
[{Head, Condition,
[{return_trace},{message, DefaultMsg}|traverse_ms_c(Body, Capture)]}
|| {Head, Condition, Body} <- MS].
%% @doc traverse a match-spec clause
traverse_ms_c([{message, true}|T], false) ->
[{message, arity}|traverse_ms_c(T, false)];
traverse_ms_c([{message, true}|T], true) ->
[{message, '$_'}|traverse_ms_c(T, true)];
traverse_ms_c([{message, Other}|T], false) when Other =/= false ->
[{message, arity}|traverse_ms_c(T, false)];
traverse_ms_c([H|T], C) ->
[traverse_ms_c(H, C)|traverse_ms_c(T, C)];
traverse_ms_c(Tuple, C) when is_tuple(Tuple) ->
list_to_tuple(traverse_ms_c(tuple_to_list(Tuple), C));
traverse_ms_c([], _) ->
[];
traverse_ms_c(Other, _) ->
Other.
err(Fmt) ->
throw({error, fmt(Fmt, [])}).
err(Fmt, Args) ->
throw({error, fmt(Fmt, Args)}).
err({1, Col}, Mod, Err) ->
throw({error, fmt("~s at column ~p", [Mod:format_error(Err), Col])}).
fmt(Fmt, Args) ->
lists:flatten(io_lib:format(Fmt, Args)).