aboutsummaryrefslogtreecommitdiffstats
path: root/lib/test_server/src/test_server_line.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/test_server/src/test_server_line.erl')
-rw-r--r--lib/test_server/src/test_server_line.erl380
1 files changed, 380 insertions, 0 deletions
diff --git a/lib/test_server/src/test_server_line.erl b/lib/test_server/src/test_server_line.erl
new file mode 100644
index 0000000000..26ef3a3040
--- /dev/null
+++ b/lib/test_server/src/test_server_line.erl
@@ -0,0 +1,380 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(test_server_line).
+
+%% User interface
+-export([get_lines/0]).
+-export([clear/0]).
+
+%% Parse transform functions
+-export([parse_transform/2]).
+-export(['$test_server_line'/3]).
+-export(['$test_server_lineQ'/3]).
+-export([trace_line/3]).
+
+-define(TEST_SERVER_LINE_SIZE, 10).
+%-define(STORAGE_FUNCTION, '$test_server_line').
+-define(STORAGE_FUNCTION, '$test_server_lineQ').
+
+-include("test_server.hrl").
+
+-record(vars, {module, % atom() Module name
+ function, % atom() Function name
+ arity, % int() Function arity
+ lines, % [int()] seen lines
+ is_guard=false, % boolean()
+ no_lines=[], % [{atom(),integer()}]
+ % Functions to exclude
+ line_trace=false
+ }).
+
+
+
+
+%% Process dictionary littering variant
+%%
+
+'$test_server_line'(Mod, Func, Line) ->
+ {Prev,Next} =
+ case get('$test_server_line') of
+ I when is_integer(I) ->
+ if 1 =< I, I < ?TEST_SERVER_LINE_SIZE -> {I,I+1};
+ true -> {?TEST_SERVER_LINE_SIZE,1}
+ end;
+ _ -> {?TEST_SERVER_LINE_SIZE,1}
+ end,
+ PrevTag = {'$test_server_line',Prev},
+ case get(PrevTag) of
+ {Mod,Func,_} -> put(PrevTag, {Mod,Func,Line});
+ _ ->
+ put({'$test_server_line',Next}, {Mod,Func,Line}),
+ put('$test_server_line', Next)
+ end, ok.
+
+test_server_line_get() ->
+ case get('$test_server_line') of
+ I when is_integer(I), 1 =< I, I =< ?TEST_SERVER_LINE_SIZE ->
+ test_server_line_get_1(?TEST_SERVER_LINE_SIZE, I, []);
+ _ -> []
+ end.
+
+test_server_line_get_1(0, _I, R) ->
+ R;
+test_server_line_get_1(Cnt, I, R) ->
+ J = if I < ?TEST_SERVER_LINE_SIZE -> I+1;
+ true -> 1 end,
+ case get({'$test_server_line',J}) of
+ undefined ->
+ %% Less than ?TEST_SERVER_LINE_SIZE number of lines stored
+ %% Start from line 1 and stop at actutual number of lines
+ case get({'$test_server_line',1}) of
+ undefined -> R; % no lines at all stored
+ E -> test_server_line_get_1(I-1,1,[E|R])
+ end;
+ E ->
+ test_server_line_get_1(Cnt-1, J, [E|R])
+ end.
+
+test_server_line_clear() ->
+ Is = lists:seq(1,?TEST_SERVER_LINE_SIZE),
+ lists:foreach(fun (I) -> erase({'$test_server_line',I}) end, Is),
+ erase('$test_server_line'),
+ ok.
+
+
+%% Queue variant, uses just one process dictionary entry
+%%
+
+'$test_server_lineQ'(Mod, Func, Line) ->
+ case get('$test_server_lineQ') of
+ {I,Q} when is_integer(I), 1 =< I, I =< ?TEST_SERVER_LINE_SIZE ->
+ case queue:head(Q) of
+ {Mod,Func,_} ->
+ %% Replace queue head
+ put('$test_server_lineQ',
+ {I,queue:cons({Mod,Func,Line}, queue:tail(Q))});
+ _ when I < ?TEST_SERVER_LINE_SIZE ->
+ put('$test_server_lineQ',
+ {I+1,queue:cons({Mod,Func,Line}, Q)});
+ _ ->
+ %% Waste last in queue
+ put('$test_server_lineQ',
+ {I,queue:cons({Mod,Func,Line}, queue:lait(Q))})
+ end;
+ _ ->
+ Q = queue:new(),
+ put('$test_server_lineQ', {1,queue:cons({Mod,Func,Line}, Q)})
+ end, ok.
+
+%test_server_lineQ_get() ->
+% case get('$test_server_lineQ') of
+% {I,Q} when integer(I), 1 =< I, I =< ?TEST_SERVER_LINE_SIZE ->
+% queue:to_list(Q);
+% _ -> []
+% end.
+
+test_server_lineQ_clear() ->
+ erase('$test_server_lineQ'),
+ ok.
+
+
+%% Get line - check if queue or dictionary is used, then get the lines
+%%
+
+get_lines() ->
+ case get('$test_server_lineQ') of
+ {I,Q} when is_integer(I), 1 =< I, I =< ?TEST_SERVER_LINE_SIZE ->
+ queue:to_list(Q);
+ _ ->
+ test_server_line_get()
+ end.
+
+%% Clear all dictionary entries
+%%
+clear() ->
+ test_server_line_clear(),
+ test_server_lineQ_clear().
+
+
+trace_line(Mod,Func,Line) ->
+ io:format(lists:concat([Mod,":",Func,",",integer_to_list(Line),": ~p"]),
+ [erlang:now()]).
+
+
+%%%=================================================================
+%%%========= **** PARSE TRANSFORM **** ========================
+%%%=================================================================
+parse_transform(Forms, _Options) ->
+ transform(Forms, _Options).
+
+%% forms(Fs) -> lists:map(fun (F) -> form(F) end, Fs).
+
+transform(Forms, _Options)->
+ Vars0 = #vars{},
+ {ok, MungedForms, _Vars} = transform(Forms, [], Vars0),
+ MungedForms.
+
+
+transform([Form|Forms], MungedForms, Vars) ->
+ case munge(Form, Vars) of
+ ignore ->
+ transform(Forms, MungedForms, Vars);
+ {MungedForm, Vars2} ->
+ transform(Forms, [MungedForm|MungedForms], Vars2)
+ end;
+transform([], MungedForms, Vars) ->
+ {ok, lists:reverse(MungedForms), Vars}.
+
+%% This code traverses the abstract code, stored as the abstract_code
+%% chunk in the BEAM file, as described in absform(3) for Erlang/OTP R8B
+%% (Vsn=abstract_v2).
+%% The abstract format after preprocessing differs slightly from the abstract
+%% format given eg using epp:parse_form, this has been noted in comments.
+munge(Form={attribute,_,module,Module}, Vars) ->
+ Vars2 = Vars#vars{module=Module},
+ {Form, Vars2};
+
+munge(Form={attribute,_,no_lines,Funcs}, Vars) ->
+ Vars2 = Vars#vars{no_lines=Funcs},
+ {Form, Vars2};
+
+munge(Form={attribute,_,line_trace,_}, Vars) ->
+ Vars2 = Vars#vars{line_trace=true},
+ {Form, Vars2};
+
+munge({function,0,module_info,_Arity,_Clauses}, _Vars) ->
+ ignore; % module_info will be added again when the forms are recompiled
+munge(Form = {function,Line,Function,Arity,Clauses}, Vars) ->
+ case lists:member({Function,Arity},Vars#vars.no_lines) of
+ true ->
+ %% Line numbers in this function shall not be stored
+ {Form,Vars};
+ false ->
+ Vars2 = Vars#vars{function=Function,
+ arity=Arity,
+ lines=[]},
+ {MungedClauses, Vars3} = munge_clauses(Clauses, Vars2, []),
+ {{function,Line,Function,Arity,MungedClauses}, Vars3}
+ end;
+munge(Form, Vars) -> % attributes
+ {Form, Vars}.
+
+munge_clauses([{clause,Line,Pattern,Guards,Body}|Clauses], Vars, MClauses) ->
+ {MungedGuards, _Vars} = munge_exprs(Guards, Vars#vars{is_guard=true},[]),
+ {MungedBody, Vars2} = munge_body(Body, Vars, []),
+ munge_clauses(Clauses, Vars2,
+ [{clause,Line,Pattern,MungedGuards,MungedBody}|
+ MClauses]);
+munge_clauses([], Vars, MungedClauses) ->
+ {lists:reverse(MungedClauses), Vars}.
+
+munge_body([Expr|Body], Vars, MungedBody) ->
+ %% Here is the place to add a call to storage function!
+ Line = element(2, Expr),
+ Lines = Vars#vars.lines,
+ case lists:member(Line,Lines) of
+ true -> % already a bump at this line!
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ munge_body(Body, Vars2, [MungedExpr|MungedBody]);
+ false ->
+ Bump = {call, 0, {remote,0,
+ {atom,0,?MODULE},
+ {atom,0,?STORAGE_FUNCTION}},
+ [{atom,0,Vars#vars.module},
+ {atom, 0, Vars#vars.function},
+ {integer, 0, Line}]},
+ Lines2 = [Line|Lines],
+
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars#vars{lines=Lines2}),
+ MungedBody2 =
+ if Vars#vars.line_trace ->
+ LineTrace = {call, 0, {remote,0,
+ {atom,0,?MODULE},
+ {atom,0,trace_line}},
+ [{atom,0,Vars#vars.module},
+ {atom, 0, Vars#vars.function},
+ {integer, 0, Line}]},
+ [MungedExpr,LineTrace,Bump|MungedBody];
+ true ->
+ [MungedExpr,Bump|MungedBody]
+ end,
+ munge_body(Body, Vars2, MungedBody2)
+ end;
+munge_body([], Vars, MungedBody) ->
+ {lists:reverse(MungedBody), Vars}.
+
+munge_expr({match,Line,ExprL,ExprR}, Vars) ->
+ {MungedExprL, Vars2} = munge_expr(ExprL, Vars),
+ {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),
+ {{match,Line,MungedExprL,MungedExprR}, Vars3};
+munge_expr({tuple,Line,Exprs}, Vars) ->
+ {MungedExprs, Vars2} = munge_exprs(Exprs, Vars, []),
+ {{tuple,Line,MungedExprs}, Vars2};
+munge_expr({record,Line,Expr,Exprs}, Vars) ->
+ %% Only for Vsn=raw_abstract_v1
+ {MungedExprName, Vars2} = munge_expr(Expr, Vars),
+ {MungedExprFields, Vars3} = munge_exprs(Exprs, Vars2, []),
+ {{record,Line,MungedExprName,MungedExprFields}, Vars3};
+munge_expr({record_field,Line,ExprL,ExprR}, Vars) ->
+ %% Only for Vsn=raw_abstract_v1
+ {MungedExprL, Vars2} = munge_expr(ExprL, Vars),
+ {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),
+ {{record_field,Line,MungedExprL,MungedExprR}, Vars3};
+munge_expr({cons,Line,ExprH,ExprT}, Vars) ->
+ {MungedExprH, Vars2} = munge_expr(ExprH, Vars),
+ {MungedExprT, Vars3} = munge_expr(ExprT, Vars2),
+ {{cons,Line,MungedExprH,MungedExprT}, Vars3};
+munge_expr({op,Line,Op,ExprL,ExprR}, Vars) ->
+ {MungedExprL, Vars2} = munge_expr(ExprL, Vars),
+ {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),
+ {{op,Line,Op,MungedExprL,MungedExprR}, Vars3};
+munge_expr({op,Line,Op,Expr}, Vars) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ {{op,Line,Op,MungedExpr}, Vars2};
+munge_expr({'catch',Line,Expr}, Vars) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ {{'catch',Line,MungedExpr}, Vars2};
+munge_expr({call,Line1,{remote,Line2,ExprM,ExprF},Exprs},
+ Vars) when Vars#vars.is_guard==false->
+ {MungedExprM, Vars2} = munge_expr(ExprM, Vars),
+ {MungedExprF, Vars3} = munge_expr(ExprF, Vars2),
+ {MungedExprs, Vars4} = munge_exprs(Exprs, Vars3, []),
+ {{call,Line1,{remote,Line2,MungedExprM,MungedExprF},MungedExprs}, Vars4};
+munge_expr({call,Line1,{remote,_Line2,_ExprM,ExprF},Exprs},
+ Vars) when Vars#vars.is_guard==true ->
+ %% Difference in abstract format after preprocessing: BIF calls in guards
+ %% are translated to {remote,...} (which is not allowed as source form)
+ %% NOT NECESSARY FOR Vsn=raw_abstract_v1
+ munge_expr({call,Line1,ExprF,Exprs}, Vars);
+munge_expr({call,Line,Expr,Exprs}, Vars) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ {MungedExprs, Vars3} = munge_exprs(Exprs, Vars2, []),
+ {{call,Line,MungedExpr,MungedExprs}, Vars3};
+munge_expr({lc,Line,Expr,LC}, Vars) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ {MungedLC, Vars3} = munge_lc(LC, Vars2, []),
+ {{lc,Line,MungedExpr,MungedLC}, Vars3};
+munge_expr({block,Line,Body}, Vars) ->
+ {MungedBody, Vars2} = munge_body(Body, Vars, []),
+ {{block,Line,MungedBody}, Vars2};
+munge_expr({'if',Line,Clauses}, Vars) ->
+ {MungedClauses,Vars2} = munge_clauses(Clauses, Vars, []),
+ {{'if',Line,MungedClauses}, Vars2};
+munge_expr({'case',Line,Expr,Clauses}, Vars) ->
+ {MungedExpr,Vars2} = munge_expr(Expr,Vars),
+ {MungedClauses,Vars3} = munge_clauses(Clauses, Vars2, []),
+ {{'case',Line,MungedExpr,MungedClauses}, Vars3};
+munge_expr({'receive',Line,Clauses}, Vars) ->
+ {MungedClauses,Vars2} = munge_clauses(Clauses, Vars, []),
+ {{'receive',Line,MungedClauses}, Vars2};
+munge_expr({'receive',Line,Clauses,Expr,Body}, Vars) ->
+ {MungedClauses,Vars2} = munge_clauses(Clauses, Vars, []),
+ {MungedExpr, Vars3} = munge_expr(Expr, Vars2),
+ {MungedBody, Vars4} = munge_body(Body, Vars3, []),
+ {{'receive',Line,MungedClauses,MungedExpr,MungedBody}, Vars4};
+munge_expr({'try',Line,Exprs,Clauses,CatchClauses,After}, Vars) ->
+ {MungedExprs, Vars1} = munge_exprs(Exprs, Vars, []),
+ {MungedClauses, Vars2} = munge_clauses(Clauses, Vars1, []),
+ {MungedCatchClauses, Vars3} = munge_clauses(CatchClauses, Vars2, []),
+ {MungedAfter, Vars4} = munge_body(After, Vars3, []),
+ {{'try',Line,MungedExprs,MungedClauses,MungedCatchClauses,MungedAfter},
+ Vars4};
+%% Difference in abstract format after preprocessing: Funs get an extra
+%% element Extra.
+%% NOT NECESSARY FOR Vsn=raw_abstract_v1
+munge_expr({'fun',Line,{function,Name,Arity},_Extra}, Vars) ->
+ {{'fun',Line,{function,Name,Arity}}, Vars};
+munge_expr({'fun',Line,{clauses,Clauses},_Extra}, Vars) ->
+ {MungedClauses,Vars2}=munge_clauses(Clauses, Vars, []),
+ {{'fun',Line,{clauses,MungedClauses}}, Vars2};
+munge_expr({'fun',Line,{clauses,Clauses}}, Vars) ->
+ %% Only for Vsn=raw_abstract_v1
+ {MungedClauses,Vars2}=munge_clauses(Clauses, Vars, []),
+ {{'fun',Line,{clauses,MungedClauses}}, Vars2};
+munge_expr(Form, Vars) -> % var|char|integer|float|string|atom|nil|bin|eof
+ {Form, Vars}.
+
+munge_exprs([Expr|Exprs], Vars, MungedExprs) when Vars#vars.is_guard==true,
+ is_list(Expr) ->
+ {MungedExpr, _Vars} = munge_exprs(Expr, Vars, []),
+ munge_exprs(Exprs, Vars, [MungedExpr|MungedExprs]);
+munge_exprs([Expr|Exprs], Vars, MungedExprs) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ munge_exprs(Exprs, Vars2, [MungedExpr|MungedExprs]);
+munge_exprs([], Vars, MungedExprs) ->
+ {lists:reverse(MungedExprs), Vars}.
+
+munge_lc([{generate,Line,Pattern,Expr}|LC], Vars, MungedLC) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ munge_lc(LC, Vars2, [{generate,Line,Pattern,MungedExpr}|MungedLC]);
+munge_lc([Expr|LC], Vars, MungedLC) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ munge_lc(LC, Vars2, [MungedExpr|MungedLC]);
+munge_lc([], Vars, MungedLC) ->
+ {lists:reverse(MungedLC), Vars}.
+
+
+
+
+
+
+
+
+
+