From 2be4f6472ccd2a05cfba75495863fcd7d2cd95a9 Mon Sep 17 00:00:00 2001 From: Pedram Nimreezi Date: Sun, 3 Jun 2018 06:11:19 -0400 Subject: [PATCH] Allow optional statistics --- src/glc.erl | 306 ++++++++++++++++++++++++++++++++++++++--------- src/glc_code.erl | 151 +++++++++++++---------- 2 files changed, 335 insertions(+), 122 deletions(-) diff --git a/src/glc.erl b/src/glc.erl index 8aec223..7d85c0a 100644 --- a/src/glc.erl +++ b/src/glc.erl @@ -203,23 +203,29 @@ union(Queries) -> %% The counters are reset by default, unless Reset is set to false -spec compile(atom(), glc_ops:op() | [glc_ops:op()]) -> {ok, atom()}. compile(Module, Query) -> - compile(Module, Query, undefined, true). + compile(Module, Query, [{statistics, true}]). -spec compile(atom(), glc_ops:op() | [glc_ops:op()], boolean()) -> {ok, atom()}. -compile(Module, Query, Reset) when is_boolean(Reset) -> - compile(Module, Query, undefined, Reset); +%compile(Module, Query, Reset) when is_boolean(Reset) -> +% compile(Module, Query, undefined, Reset); compile(Module, Query, undefined) -> - compile(Module, Query, undefined, true); + compile(Module, Query, [{statistics, true}]); compile(Module, Query, Store) when is_list(Store) -> - compile(Module, Query, Store, true). + case lists:keyfind(statistics, 1, Store) of + {_, true} -> + compile(Module, Query, Store, true); + _ -> + compile(Module, Query, Store, false) + end. -compile(Module, Query, Store, Reset) -> - {ok, ModuleData} = module_data(Module, Query, Store), - case glc_code:compile(Module, ModuleData) of - {ok, Module} when Reset -> +compile(Module, Query, Store, Stats) -> + {ok, ModuleData} = module_data(Module, Query, Store, Stats), + case glc_code:compile(Module, ModuleData, Stats) of + {ok, Module} when is_boolean(Stats), Stats =:= true -> reset_counters(Module), {ok, Module}; {ok, Module} -> + ok = take_down(Module, Stats), {ok, Module} end. @@ -284,26 +290,41 @@ job_input(Module) -> job_time(Module) -> Module:info(job_time). -%% @doc Release a compiled query. -%% -%% This releases all resources allocated by a compiled query. The query name -%% is expected to be associated with an existing query module. Calling this -%% function will shutdown all relevant processes and purge/delete the module. --spec delete(atom()) -> ok. -delete(Module) -> - Params = params_name(Module), +-spec take_down(atom(), boolean()) -> ok. +take_down(Module, false) -> Counts = counts_name(Module), - ManageParams = manage_params_name(Module), ManageCounts = manage_counts_name(Module), _ = [ begin ok = supervisor:terminate_child(Sup, Name), ok = supervisor:delete_child(Sup, Name) end || {Sup, Name} <- - [{gr_manager_sup, ManageParams}, {gr_manager_sup, ManageCounts}, - {gr_param_sup, Params}, {gr_counter_sup, Counts}] + [{gr_manager_sup, ManageCounts}, + {gr_counter_sup, Counts}] ], + ok; +take_down(Module, true) -> + Params = params_name(Module), + ManageParams = manage_params_name(Module), + catch (take_down(Module, false)), + + _ = [ begin + ok = supervisor:terminate_child(Sup, Name), + ok = supervisor:delete_child(Sup, Name) + end || {Sup, Name} <- + [{gr_manager_sup, ManageParams}, + {gr_param_sup, Params}] + ], + ok. +%% @doc Release a compiled query. +%% +%% This releases all resources allocated by a compiled query. The query name +%% is expected to be associated with an existing query module. Calling this +%% function will shutdown all relevant processes and purge/delete the module. +-spec delete(atom()) -> ok. +delete(Module) -> + ok = take_down(Module, true), code:soft_purge(Module), code:delete(Module), ok. @@ -330,8 +351,8 @@ prepare_store(Store) -> end, Store). %% @private Map a query to a module data term. --spec module_data(atom(), term(), term()) -> {ok, #module{}}. -module_data(Module, Query, Store) -> +-spec module_data(atom(), term(), term(), boolean()) -> {ok, #module{}}. +module_data(Module, Query, Store, Stats) -> %% terms in the query which are not valid arguments to the %% erl_syntax:abstract/1 functions are stored in ETS. %% the terms are only looked up once they are necessary to @@ -342,13 +363,13 @@ module_data(Module, Query, Store) -> %% the abstract_tables/1 function expects a list of name-atom pairs. %% tables are referred to by name in the generated code. the table/1 %% function maps names to registered processes response for those tables. - Tables = module_tables(Module), + Tables = module_tables(Module, Stats), Query2 = glc_lib:reduce(Query), Store2 = prepare_store(Store), {ok, #module{'query'=Query, tables=Tables, qtree=Query2, store=Store2}}. %% @private Create a data managed supervised process for params, counter tables -module_tables(Module) -> +module_tables(Module, _Stats) -> Params = params_name(Module), Counts = counts_name(Module), ManageParams = manage_params_name(Module), @@ -414,7 +435,7 @@ start() -> -include_lib("eunit/include/eunit.hrl"). setup_query(Module, Query) -> - setup_query(Module, Query, undefined). + setup_query(Module, Query, [{statistics, true}]). setup_query(Module, Query, Store) -> ?assertNot(erlang:module_loaded(Module)), @@ -460,9 +481,18 @@ events_test_() -> ?assertEqual({null, false}, Mod:info('query')) end }, + {"no init counters test", + fun() -> + {compiled, Mod} = setup_query(testmod4a, glc:null(false), [{statistics, false}]), + glc:handle(Mod, gre:make([], [list])), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(output)) + end + }, {"init counters test", fun() -> - {compiled, Mod} = setup_query(testmod4, glc:null(false)), + {compiled, Mod} = setup_query(testmod4b, glc:null(false)), ?assertEqual(0, Mod:info(input)), ?assertEqual(0, Mod:info(filter)), ?assertEqual(0, Mod:info(output)) @@ -639,7 +669,7 @@ events_test_() -> {"with function storage test", fun() -> Self = self(), - Store = [{stored, value}], + Store = [{stored, value}, {statistics, true}], {compiled, Mod} = setup_query(testmod15, glc:with(glc:eq(a, 1), fun(Event, EStore) -> Self ! {gre:fetch(a, Event), EStore} end), @@ -651,7 +681,7 @@ events_test_() -> }, {"delete test", fun() -> - {compiled, Mod} = setup_query(testmod16, glc:null(false)), + {compiled, Mod} = setup_query(testmod16a, glc:null(false)), ?assert(is_atom(Mod:table(params))), ?assertMatch([_|_], gr_param:info(Mod:table(params))), ?assert(is_list(code:which(Mod))), @@ -669,6 +699,27 @@ events_test_() -> ?assertEqual(undefined, whereis(manage_counts_name(Mod))) end }, + {"delete test no stats", + fun() -> + {compiled, Mod} = setup_query(testmod16b, glc:null(false), + [{statistics, false}]), + ?assert(is_atom(Mod:table(params))), + ?assertMatch([_|_], gr_param:info(Mod:table(params))), + ?assert(is_list(code:which(Mod))), + ?assert(is_pid(whereis(params_name(Mod)))), + ?assertNot(is_pid(whereis(counts_name(Mod)))), + ?assert(is_pid(whereis(manage_params_name(Mod)))), + ?assertNot(is_pid(whereis(manage_counts_name(Mod)))), + + glc:delete(Mod), + + ?assertEqual(non_existing, code:which(Mod)), + ?assertEqual(undefined, whereis(params_name(Mod))), + ?assertEqual(undefined, whereis(counts_name(Mod))), + ?assertEqual(undefined, whereis(manage_params_name(Mod))), + ?assertEqual(undefined, whereis(manage_counts_name(Mod))) + end + }, {"reset counters test", fun() -> {compiled, Mod} = setup_query(testmod17, @@ -717,16 +768,35 @@ events_test_() -> ?assertEqual(7, length(gr_counter:list(Mod:table(counters)))) end }, + {"ets data recovery test no stats", + fun() -> + Self = self(), + {compiled, Mod} = setup_query(testmod18b, + glc:with(glc:eq(a, 1), fun(Event) -> Self ! gre:fetch(a, Event) end), + [{statistics, false}]), + glc:handle(Mod, gre:make([{a,1}], [list])), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(1, receive Msg -> Msg after 0 -> notcalled end), + ?assertEqual(1, length(gr_param:list(Mod:table(params)))), + true = exit(whereis(Mod:table(params)), kill), + ?assertEqual(undefined, whereis(Mod:table(counters))), + ?assertEqual(0, Mod:info(input)), + glc:handle(Mod, gre:make([{'a', 1}], [list])), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(1, length(gr_param:list(Mod:table(params)))) + end + }, {"run timed job test", fun() -> Self = self(), - Store = [{stored, value}], - Runtime = 0.15, + Store = [{stored, value}, {statistics, true}], + Runtime = 0.015, {compiled, Mod} = setup_query(testmod19, glc:gt(runtime, Runtime), Store), glc:run(Mod, fun(Event, EStore) -> - timer:sleep(100), + timer:sleep(10), Self ! {gre:fetch(a, Event), EStore} end, gre:make([{a,1}], [list])), ?assertEqual(0, Mod:info(output)), @@ -739,7 +809,7 @@ events_test_() -> Store), glc:handle(Mod, gre:make([{'a', 1}], [list])), glc:run(Mod, fun(Event, EStore) -> - timer:sleep(200), + timer:sleep(30), Self ! {gre:fetch(a, Event), EStore} end, gre:make([{a,2}], [list])), ?assertEqual(1, Mod:info(output)), @@ -750,8 +820,11 @@ events_test_() -> }, {"reset job counters", fun() -> - {compiled, Mod} = setup_query(testmod20, - glc:any([glc:eq(a, 1), glc:gt(runtime, 0.15)])), + Self = self(), + Store = [{stored, value}, {statistics, true}], + + {compiled, Mod} = setup_query(testmod20a, + glc:any([glc:eq(a, 1), glc:gt(runtime, 0.015)]), Store), glc:handle(Mod, gre:make([{'a', 2}], [list])), glc:handle(Mod, gre:make([{'b', 1}], [list])), ?assertEqual(2, Mod:info(input)), @@ -762,17 +835,20 @@ events_test_() -> ?assertEqual(3, Mod:info(filter)), ?assertEqual(1, Mod:info(output)), - Self = self(), glc:run(Mod, fun(Event, EStore) -> - timer:sleep(100), + timer:sleep(20), Self ! {gre:fetch(a, Event), EStore} end, gre:make([{a,1}], [list])), ?assertEqual(2, Mod:info(output)), ?assertEqual(3, Mod:info(filter)), - ?assertEqual(1, receive {Msg, undefined} -> Msg after 0 -> notcalled end), + receive {Msg, _} -> + ?assertEqual(1, Msg) + after 0 -> + erlang:error(notcalled) + end, {_, Msg1} = glc:run(Mod, fun(_Event, _EStore) -> - timer:sleep(200), + timer:sleep(20), {error, badtest} end, gre:make([{a,1}], [list])), @@ -784,7 +860,7 @@ events_test_() -> ?assertEqual({error, badtest}, Msg1), {_, Msg2} = glc:run(Mod, fun(_Event, _EStore) -> - timer:sleep(20), + timer:sleep(10), {ok, goodtest} end, gre:make([{a,2}], [list])), @@ -840,10 +916,110 @@ events_test_() -> ?assertEqual(0, Mod:info(job_run)) end }, + {"reset job counters with no statistics", + fun() -> + Self = self(), + Store = [{stored, value}, {statistics, false}], + + {compiled, Mod} = setup_query(testmod20b, + glc:any([glc:eq(a, 1), glc:gt(runtime, 0.15)]), Store), + glc:handle(Mod, gre:make([{'a', 2}], [list])), + glc:handle(Mod, gre:make([{'b', 1}], [list])), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(filter)), + glc:handle(Mod, gre:make([{'a', 1}], [list])), + glc:handle(Mod, gre:make([{'b', 2}], [list])), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(output)), + + glc:run(Mod, fun(Event, EStore) -> + Self ! {gre:fetch(a, Event), EStore} + end, gre:make([{a,1}], [list])), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(0, Mod:info(filter)), + + % not working? + % ?assertEqual(1, receive {Msg, undefined} -> Msg after 0 -> notcalled end), + receive {Msg, _} -> + ?assertEqual(1, Msg) + after 0 -> + erlang:error(notcalled) + end, + + {_, Msg1} = glc:run(Mod, fun(_Event, _EStore) -> + timer:sleep(20), + {error, badtest} + + end, gre:make([{a,1}], [list])), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(job_input)), + ?assertEqual(0, Mod:info(job_error)), + ?assertEqual(0, Mod:info(job_run)), + ?assertEqual({error, badtest}, Msg1), + + {_, Msg2} = glc:run(Mod, fun(_Event, _EStore) -> + timer:sleep(20), + {ok, goodtest} + + end, gre:make([{a,2}], [list])), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(job_input)), + ?assertEqual(0, Mod:info(job_error)), + ?assertEqual(0, Mod:info(job_run)), + ?assertEqual({ok, goodtest}, Msg2), + + + glc:reset_counters(Mod, input), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(0, Mod:info(job_input)), + ?assertEqual(0, Mod:info(job_error)), + ?assertEqual(0, Mod:info(job_run)), + glc:reset_counters(Mod, filter), + ?assertEqual(0, glc:input(Mod)), + ?assertEqual(0, glc:filter(Mod)), + ?assertEqual(0, glc:output(Mod)), + ?assertEqual(0, glc:job_input(Mod)), + ?assertEqual(0, glc:job_error(Mod)), + ?assertEqual(0, glc:job_run(Mod)), + glc:reset_counters(Mod, output), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(0, Mod:info(job_input)), + ?assertEqual(0, Mod:info(job_error)), + ?assertEqual(0, Mod:info(job_run)), + glc:reset_counters(Mod, job_input), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(0, Mod:info(job_input)), + ?assertEqual(0, Mod:info(job_error)), + ?assertEqual(0, Mod:info(job_run)), + glc:reset_counters(Mod, job_error), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(0, Mod:info(job_input)), + ?assertEqual(0, Mod:info(job_error)), + ?assertEqual(0, Mod:info(job_run)), + glc:reset_counters(Mod, job_run), + ?assertEqual(0, Mod:info(input)), + ?assertEqual(0, Mod:info(filter)), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(0, Mod:info(job_input)), + ?assertEqual(0, Mod:info(job_error)), + ?assertEqual(0, Mod:info(job_run)) + end + }, {"variable storage test", fun() -> - {compiled, Mod} = setup_query(testmod20a, - glc:eq(a, 2), [{stream, time}]), + {compiled, Mod} = setup_query(testmod20c, + glc:eq(a, 2), [{stream, time}, {statistics, true}]), glc:handle(Mod, gre:make([{'a', 2}], [list])), glc:handle(Mod, gre:make([{'b', 1}], [list])), ?assertEqual(2, Mod:info(input)), @@ -855,17 +1031,35 @@ events_test_() -> ?assertEqual({error, undefined}, glc:get(Mod, beam)) end }, + {"with multi function any test no stats", + fun() -> + Self = self(), + Store = [{stored, value}, {statistics, false}], + + G1 = glc:with(glc:eq(a, 1), fun(_Event, EStore) -> + Self ! {a, EStore} end), + G2 = glc:with(glc:eq(b, 2), fun(_Event, EStore) -> + Self ! {b, EStore} end), + + {compiled, Mod} = setup_query(testmod20d, any([G1, G2]), + Store), + glc:handle(Mod, gre:make([{a,1}], [list])), + ?assertEqual(0, Mod:info(output)), + ?assertEqual(a, receive {Msg, _Store} -> Msg after 0 -> notcalled end), + ?assertEqual(b, receive {Msg, _Store} -> Msg after 0 -> notcalled end) + end + }, {"with multi function any test", fun() -> Self = self(), - Store = [{stored, value}], + Store = [{stored, value}, {statistics, true}], G1 = glc:with(glc:eq(a, 1), fun(_Event, EStore) -> Self ! {a, EStore} end), G2 = glc:with(glc:eq(b, 2), fun(_Event, EStore) -> Self ! {b, EStore} end), - {compiled, Mod} = setup_query(testmod20b, any([G1, G2]), + {compiled, Mod} = setup_query(testmod20e, any([G1, G2]), Store), glc:handle(Mod, gre:make([{a,1}], [list])), ?assertEqual(1, Mod:info(output)), @@ -876,7 +1070,7 @@ events_test_() -> {"with multi function all test", fun() -> Self = self(), - Store = [{stored, value}], + Store = [{stored, value}, {statistics, true}], G1 = glc:with(glc:eq(a, 1), fun(_Event, EStore) -> Self ! {a, EStore} end), @@ -903,7 +1097,7 @@ events_test_() -> {"with multi-function output match test", fun() -> Self = self(), - Store = [{stored, value}], + Store = [{stored, value}, {statistics, true}], {compiled, Mod} = setup_query(testmod22, [glc:with(glc:eq(a, 1), fun(Event, _EStore) -> @@ -920,7 +1114,7 @@ events_test_() -> {"with multi-function output double-match test", fun() -> Self = self(), - Store = [{stored, value}], + Store = [{stored, value}, {statistics, true}], {compiled, Mod} = setup_query(testmod23, [glc:with(glc:eq(a, 1), fun(Event, _EStore) -> Self ! {a, gre:fetch(a, Event)} end), @@ -936,7 +1130,7 @@ events_test_() -> {"with multi function complex match test", fun() -> Self = self(), - Store = [{stored, value}], + Store = [{stored, value}, {statistics, true}], G1 = glc:with(glc:gt(r, 0.1), fun(_Event, EStore) -> Self ! {a, EStore} end), @@ -979,20 +1173,20 @@ events_test_() -> {"with single-function run test", fun() -> Self = self(), - Store = [{stored, value}], + Store = [{stored, value}, {statistics, true}], {compiled, Mod1} = setup_query(testmod25a, - glc:with(glc:all([glc:gt(runtime, 0.15), glc:lt(a, 3)]), fun(Event, EStore) -> + glc:with(glc:all([glc:gt(runtime, 0.015), glc:lt(a, 3)]), fun(Event, EStore) -> Self ! {gre:fetch(a, Event), EStore} end), Store), - glc:run(Mod1, fun(_Event, _EStore) -> timer:sleep(200), ok end, gre:make([{a, 2}], [list])), + glc:run(Mod1, fun(_Event, _EStore) -> timer:sleep(20), ok end, gre:make([{a, 2}], [list])), ?assertEqual(1, Mod1:info(output)), ?assertEqual(2, receive {Msg, Store} -> Msg after 250 -> notcalled end), {compiled, Mod2} = setup_query(testmod25b, - glc:with(glc:all([glc:gt(runtime, 0.15), glc:lt(a, 3)]), fun(Event, EStore) -> + glc:with(glc:all([glc:gt(runtime, 0.015), glc:lt(a, 3)]), fun(Event, EStore) -> Self ! {gre:fetch(a, Event), EStore} end), Store), {_, {error, later}} = glc:run(Mod2, fun(_Event, _EStore) -> - timer:sleep(200), + timer:sleep(20), erlang:exit(later) end, gre:make([{a, 2}], [list])), ?assertEqual(1, Mod2:info(output)), @@ -1003,9 +1197,9 @@ events_test_() -> {"with multi-function output run error test", fun() -> Self = self(), - Store = [{stored, value}], + Store = [{stored, value}, {statistics, true}], {compiled, Mod} = setup_query(testmod26, - [glc:with(glc:gt(runtime, 0.15), fun(Event, _EStore) -> + [glc:with(glc:gt(runtime, 0.015), fun(Event, _EStore) -> Self ! {a, gre:fetch(b, Event)} end), glc:with(glc:eq(c, 3), fun(Event, _EStore) -> @@ -1023,7 +1217,7 @@ events_test_() -> Store), Event = gre:make([{a,1}, {b, 3}, {c, 4}], [list]), {_, {error, bye}} = glc:run(Mod, fun(_Event, _EStore) -> - timer:sleep(200), + timer:sleep(20), erlang:error(bye) end, Event), ?assertEqual(3, Mod:info(output)), @@ -1036,7 +1230,7 @@ events_test_() -> fun() -> Self = self(), XPid = spawn(fun() -> receive {msg, Msg, Pid} -> Self ! {Msg, Pid} end end), - Store = [{stored, XPid}], + Store = [{stored, XPid}, {statistics, true}], {compiled, Mod} = setup_query(testmod27, glc:with(glc:eq(a, 1), fun(Event, _EStore) -> {ok, Pid} = glc:get(testmod27, stored), diff --git a/src/glc_code.erl b/src/glc_code.erl index a749e38..502b7dc 100644 --- a/src/glc_code.erl +++ b/src/glc_code.erl @@ -1,8 +1,8 @@ %% @doc Code generation functions. -module(glc_code). --compile({nowarn_unused_function, {abstract_module,2}}). +-compile({nowarn_unused_function, {abstract_module,3}}). -compile({nowarn_unused_function, {abstract_tables,1}}). --compile({nowarn_unused_function, {abstract_reset,0}}). +-compile({nowarn_unused_function, {abstract_reset,1}}). -compile({nowarn_unused_function, {abstract_filter,3}}). -compile({nowarn_unused_function, {abstract_filter_,4}}). -compile({nowarn_unused_function, {abstract_opfilter,6}}). @@ -21,33 +21,34 @@ -export([ - compile/2 + compile/3 ]). -define(erl, erl_syntax). -record(module, { 'query' :: term(), - tables :: [{atom(), atom()}], - qtree :: term(), - store :: term() + tables :: [{atom(), atom()}], + qtree :: term(), + store :: term() }). -type syntaxTree() :: erl_syntax:syntaxTree(). -record(state, { - event = undefined :: syntaxTree(), - fields = [] :: [{atom(), syntaxTree()}], - fieldc = 0 :: non_neg_integer(), - paramvars = [] :: [{term(), syntaxTree()}], - paramstab = undefined :: atom(), - countstab = undefined :: atom() + event = undefined :: syntaxTree(), + fields = [] :: [{atom(), syntaxTree()}], + fieldc = 0 :: non_neg_integer(), + paramvars = [] :: [{term(), syntaxTree()}], + paramstab = undefined :: atom(), + countstab = undefined :: atom(), + statistics = false :: boolean() }). -type nextFun() :: fun((#state{}) -> [syntaxTree()]). -compile(Module, ModuleData) -> - {ok, forms, Forms} = abstract_module(Module, ModuleData), +compile(Module, ModuleData, Stats) -> + {ok, forms, Forms} = abstract_module(Module, ModuleData, Stats), {ok, Module, Binary} = compile_forms(Forms, [nowarn_unused_vars]), {ok, loaded, Module} = load_binary(Module, Binary), {ok, Module}. @@ -55,9 +56,9 @@ compile(Module, ModuleData) -> %% abstract code generation functions %% @private Generate an abstract dispatch module. --spec abstract_module(atom(), #module{}) -> {ok, forms, list()}. -abstract_module(Module, Data) -> - Forms = [?erl:revert(E) || E <- abstract_module_(Module, Data)], +-spec abstract_module(atom(), #module{}, #state{}) -> {ok, forms, list()}. +abstract_module(Module, Data, Stats) -> + Forms = [?erl:revert(E) || E <- abstract_module_(Module, Data, Stats)], case lists:keyfind(errors, 1, erl_syntax_lib:analyze_forms(Forms)) of false -> {ok, forms, Forms}; {_, []} -> {ok, forms, Forms}; @@ -65,11 +66,15 @@ abstract_module(Module, Data) -> end. %% @private Generate an abstract dispatch module. --spec abstract_module_(atom(), #module{}) -> [?erl:syntaxTree()]. +-spec abstract_module_(atom(), #module{}, #state{}) -> [?erl:syntaxTree()]. abstract_module_(Module, #module{tables=Tables, - qtree=Tree, store=Store}=Data) -> + qtree=Tree, store=Store}=Data, Stats) -> {_, ParamsTable} = lists:keyfind(params, 1, Tables), {_, CountsTable} = lists:keyfind(counters, 1, Tables), + State = #state{ event=?erl:variable("Event"), + paramstab=ParamsTable, + countstab=CountsTable, + statistics=Stats}, AbstractMod = [ %% -module(Module) ?erl:attribute(?erl:atom(module), [?erl:atom(Module)]), @@ -111,14 +116,14 @@ abstract_module_(Module, #module{tables=Tables, %% info(Name) -> Term. ?erl:function( ?erl:atom(info), - abstract_info(Data) ++ + abstract_info(Data, State) ++ [?erl:clause( [?erl:underscore()], none, [abstract_apply(erlang, error, [?erl:atom(badarg)])])]), %% reset_counters(Name) -> boolean(). ?erl:function( ?erl:atom(reset_counters), - abstract_reset() ++ + abstract_reset(State) ++ [?erl:clause( [?erl:underscore()], none, [abstract_apply(erlang, error, [?erl:atom(badarg)])])]), @@ -133,13 +138,13 @@ abstract_module_(Module, #module{tables=Tables, ?erl:function( ?erl:atom(handle), [?erl:clause([?erl:variable("Event")], none, - [abstract_count(input), + [abstract_count(input, State), ?erl:application(none, ?erl:atom(handle_), [?erl:variable("Event")])])]), ?erl:function( ?erl:atom(runjob), [?erl:clause([?erl:variable("Fun"), ?erl:variable("Event")], none, - [abstract_count(job_input), + [abstract_count(job_input, State), ?erl:application(none, ?erl:atom(job_), [?erl:variable("Fun"), ?erl:variable("Event")])])]), @@ -147,10 +152,7 @@ abstract_module_(Module, #module{tables=Tables, ?erl:function( ?erl:atom(handle_), [?erl:clause([?erl:variable("Event")], none, - abstract_filter(Tree, Data, #state{ - event=?erl:variable("Event"), - paramstab=ParamsTable, - countstab=CountsTable}))]), + abstract_filter(Tree, Data, State))]), ?erl:function( ?erl:atom(job_), [?erl:clause([?erl:variable("Fun"), @@ -169,7 +171,7 @@ abstract_module_(Module, #module{tables=Tables, )]), ?erl:function( ?erl:atom(job_result), - abstract_runjob(Data) + abstract_runjob(Data, State) ) ], %% Transform Term -> Key to Key -> Term @@ -219,7 +221,7 @@ abstract_get(#module{'query'=_Query, store=Store}) -> || {K, _} <- Store]. %% @private -abstract_runjob(#module{'query'=_Query, store=_Store}) -> +abstract_runjob(#module{'query'=_Query, store=_Store}, State) -> Time = abstract_apply(erlang, '/', [?erl:variable("Time"), ?erl:abstract(1000000)]), [?erl:clause([?erl:variable("JobResult"), @@ -235,11 +237,11 @@ abstract_runjob(#module{'query'=_Query, store=_Store}) -> ?erl:clause( [?erl:tuple([?erl:atom(error),?erl:variable("Reason")])], none, - [abstract_count(input), abstract_count(job_error), + [abstract_count(input, State), abstract_count(job_error, State), ?erl:application(none, ?erl:atom(handle_), abstract_job(Time, [?erl:tuple([?erl:atom(error), ?erl:variable("Reason")])])), - abstract_count(job_time, ?erl:variable("Time")), + abstract_count(job_time, State, ?erl:variable("Time")), ?erl:tuple([?erl:variable("Time"), ?erl:tuple([?erl:atom(error), ?erl:variable("Reason")])])]), @@ -247,10 +249,10 @@ abstract_runjob(#module{'query'=_Query, store=_Store}) -> ?erl:clause( [?erl:variable("Result")], none, - [abstract_count(input), abstract_count(job_run), + [abstract_count(input, State), abstract_count(job_run, State), ?erl:application(none, ?erl:atom(handle_), abstract_job(Time)), - abstract_count(job_time, ?erl:variable("Time")), + abstract_count(job_time, State, ?erl:variable("Time")), ?erl:tuple([?erl:variable("Time"), ?erl:variable("Result")])]) ]) @@ -270,21 +272,34 @@ abstract_job(Time, Error) -> ?erl:abstract([list])])]. %% @private Return the clauses of the info/1 function. -abstract_info(#module{'query'=Query}) -> +abstract_info(#module{'query'=Query}, State) -> [?erl:clause([?erl:abstract(K)], none, V) || {K, V} <- [ {'query', abstract_query(Query)}, - {input, abstract_getcount(input)}, - {filter, abstract_getcount(filter)}, - {output, abstract_getcount(output)}, - {job_input, abstract_getcount(job_input)}, - {job_run, abstract_getcount(job_run)}, - {job_time, abstract_getcount(job_time)}, - {job_error, abstract_getcount(job_error)} + {input, abstract_getcount(input, State)}, + {filter, abstract_getcount(filter, State)}, + {output, abstract_getcount(output, State)}, + {job_input, abstract_getcount(job_input, State)}, + {job_run, abstract_getcount(job_run, State)}, + {job_time, abstract_getcount(job_time, State)}, + {job_error, abstract_getcount(job_error, State)} ]]. -abstract_reset() -> +abstract_reset(#state{statistics=false}) -> + Reset = [?erl:abstract(0)], + [?erl:clause([?erl:abstract(K)], none, V) + || {K, V} <- [ + {all, Reset}, + {input, Reset}, + {filter, Reset}, + {output, Reset}, + {job_input, Reset}, + {job_run, Reset}, + {job_time, Reset}, + {job_error, Reset} + ]]; +abstract_reset(#state{statistics=true}) -> [?erl:clause([?erl:abstract(K)], none, V) || {K, V} <- [ {all, abstract_resetcount([input, filter, output, @@ -301,18 +316,17 @@ abstract_reset() -> %% @private Return a list of expressions to apply a filter. -%% @todo Allow mulitple functions to be specified using `with/2'. -spec abstract_filter(glc_ops:op() | [glc_ops:op()], #module{}, #state{}) -> [syntaxTree()]. abstract_filter({Type, [{with, _Cond, _Fun}|_] = I}, Data, State) when Type =:= all; Type =:= any -> Cond = glc_lib:reduce(glc:Type([Q || {with, Q, _} <- I])), abstract_filter_(Cond, _OnMatch=fun(State2) -> Funs = [ F || {with, _, F} <- I ], - [abstract_count(output)] ++ + [abstract_count(output, State)] ++ abstract_with(Funs, Data, State2) end, - _OnNomatch=fun(_State2) -> [abstract_count(filter)] end, State); + _OnNomatch=fun(_State2) -> [abstract_count(filter, State)] end, State); abstract_filter([{with, _Cond, _Fun}|_] = I, Data, State) -> - OnNomatch = fun(_State2) -> [abstract_count(filter, 0)] end, + OnNomatch = fun(_State2) -> [abstract_count(filter, State, 0)] end, Funs = lists:foldr(fun({with, Cond, Fun}, Acc) -> [{Cond, Fun, Data}|Acc] end, [], I), @@ -320,13 +334,13 @@ abstract_filter([{with, _Cond, _Fun}|_] = I, Data, State) -> abstract_filter({with, Cond, Fun}, Data, State) -> abstract_filter_(Cond, _OnMatch=fun(State2) -> - [abstract_count(output)] ++ + [abstract_count(output, State)] ++ abstract_with(Fun, Data, State2) end, - _OnNomatch=fun(_State2) -> [abstract_count(filter)] end, State); + _OnNomatch=fun(_State2) -> [abstract_count(filter, State)] end, State); abstract_filter(Cond, _Data, State) -> abstract_filter_(Cond, - _OnMatch=fun(_State2) -> [abstract_count(output)] end, - _OnNomatch=fun(_State2) -> [abstract_count(filter)] end, State). + _OnMatch=fun(_State2) -> [abstract_count(output, State)] end, + _OnNomatch=fun(_State2) -> [abstract_count(filter, State)] end, State). %% @private Return a list of expressions to apply a filter. %% A filter expects two continuation functions which generates the expressions @@ -422,13 +436,13 @@ abstract_with(Fun, Data, State) when is_function(Fun, 1); is_function(Fun, 2) - end, State). abstract_within([{H, Fun, Data}|T], OnNomatch, State) -> - OnMatch = fun(State2) -> [abstract_count(output)] ++ + OnMatch = fun(State2) -> [abstract_count(output, State)] ++ abstract_with(Fun, Data, State2) ++ abstract_within(T, OnNomatch, State2) end, abstract_filter_(H, OnMatch, _OnNomatch=fun(State2) -> - [abstract_count(filter)] ++ + [abstract_count(filter, State)] ++ abstract_within(T, OnNomatch, State2) end, State); abstract_within([], OnNomatch, State) -> @@ -485,7 +499,8 @@ abstract_getparam([_|_]=Terms, OnBound, #state{paramvars=_Params, fields=_Fields when is_list(Terms) -> {Keys, Bound} = lists:foldl(fun(Term, {Acc0, #state{paramvars=Params, - paramstab=ParamsTable}=State0}) -> + paramstab=ParamsTable, + statistics=_Stats}=State0}) -> case lists:keyfind(Term, 1, Params) of {_, _Variable} -> {Acc0, State0}; @@ -557,28 +572,32 @@ param_variable(Key) -> %% @private Return an expression to increment a counter. %% @todo Pass state record. Only Generate code if `statistics' is enabled. --spec abstract_count(atom()) -> syntaxTree(). -abstract_count(Counter) -> - abstract_count(Counter, 1). -abstract_count(Counter, Value) when is_integer(Value) -> +-spec abstract_count(atom(), #state{}) -> syntaxTree(). +abstract_count(Counter, State) -> + abstract_count(Counter, State, 1). +abstract_count(_Counter, #state{statistics=false}, Value) when is_integer(Value) -> + ?erl:abstract(Value); +abstract_count(_Counter, #state{statistics=false}, Value) -> + ?erl:abstract(Value); +abstract_count(Counter, #state{statistics=true}, Value) when is_integer(Value) -> abstract_apply(gr_counter, update_counter, [abstract_apply(table, [?erl:atom(counters)]), - ?erl:abstract(Counter), - ?erl:abstract({2,Value})]); -abstract_count(Counter, Value) -> + ?erl:abstract(Counter), + ?erl:abstract({2,Value})]); +abstract_count(Counter, #state{statistics=true}, Value) -> abstract_apply(gr_counter, update_counter, [abstract_apply(table, [?erl:atom(counters)]), ?erl:abstract(Counter), - ?erl:tuple([?erl:abstract(2), Value]) - ]). + ?erl:tuple([?erl:abstract(2), Value])]). %% @private Return an expression to get the value of a counter. %% @todo Pass state record. Only Generate code if `statistics' is enabled. --spec abstract_getcount(atom()) -> [syntaxTree()]. -abstract_getcount(Counter) when is_atom(Counter) -> - abstract_getcount(?erl:abstract(Counter)); -abstract_getcount(Counter) -> +-spec abstract_getcount(atom(), #state{}) -> [syntaxTree()]. +abstract_getcount(Counter, State) when is_atom(Counter) -> + abstract_getcount(?erl:abstract(Counter), State); +abstract_getcount(_Counter, #state{statistics = false}) -> [?erl:abstract(0)]; +abstract_getcount(Counter, #state{statistics = true}) -> [abstract_apply(gr_counter, lookup_element, [abstract_apply(table, [?erl:atom(counters)]), Counter])].