1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 2002-2013. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   21: %%%
   22: %%% Define to run outside of test server
   23: %%%
   24: %%% -define(STANDALONE,1).
   25: %%%
   26: %%%
   27: %%% Define for debug output
   28: %%%
   29: %%% -define(debug,1).
   30: 
   31: -module(cprof_SUITE).
   32: 
   33: %% Exported end user tests
   34: -export([basic_test/0, on_load_test/1, modules_test/1]).
   35: 
   36: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   37: %% Test server related stuff
   38: %%
   39: 
   40: -ifdef(STANDALONE).
   41: -define(config(A,B),config(A,B)).
   42: -export([config/2]).
   43: -else.
   44: -include_lib("test_server/include/test_server.hrl").
   45: -endif.
   46:  
   47: -ifdef(debug).
   48: -ifdef(STANDALONE).
   49: -define(line, erlang:display({?MODULE,?LINE}), ).
   50: -endif.
   51: -define(dbgformat(A,B),io:format(A,B)).
   52: -else.
   53: -ifdef(STANDALONE).
   54: -define(line, noop, ).
   55: -endif.
   56: -define(dbgformat(A,B),noop).
   57: -endif.
   58:  
   59: -ifdef(STANDALONE).
   60: config(priv_dir, _) ->
   61:     ".";
   62: config(data_dir, _) ->
   63:     "cprof_SUITE_data".
   64: -else.
   65: %% When run in test server.
   66: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   67: 	 init_per_group/2,end_per_group/2, 
   68: 	 init_per_testcase/2, end_per_testcase/2, 
   69: 	 not_run/1]).
   70: -export([basic/1, on_load/1, modules/1]).
   71: 	 
   72: init_per_testcase(_Case, Config) ->
   73:     ?line Dog=test_server:timetrap(test_server:seconds(30)),
   74:     [{watchdog, Dog}|Config].
   75: 
   76: end_per_testcase(_Case, Config) ->
   77:     erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
   78:     erlang:trace_pattern(on_load, false, [local,meta,call_count]),
   79:     erlang:trace(all, false, [all]),
   80:     Dog=?config(watchdog, Config),
   81:     test_server:timetrap_cancel(Dog),
   82:     ok.
   83: 
   84: suite() -> [{ct_hooks,[ts_install_cth]}].
   85: 
   86: all() -> 
   87:     case test_server:is_native(cprof_SUITE) of
   88: 	true -> [not_run];
   89: 	false -> [basic, on_load, modules]
   90:     end.
   91: 
   92: groups() -> 
   93:     [].
   94: 
   95: init_per_suite(Config) ->
   96:     Config.
   97: 
   98: end_per_suite(_Config) ->
   99:     ok.
  100: 
  101: init_per_group(_GroupName, Config) ->
  102:     Config.
  103: 
  104: end_per_group(_GroupName, Config) ->
  105:     Config.
  106: 
  107: 
  108: not_run(Config) when is_list(Config) -> 
  109:     {skipped,"Native code"}.
  110: 
  111: basic(suite) ->
  112:     [];
  113: basic(doc) ->
  114:     ["Tests basic profiling"];
  115: basic(Config) when is_list(Config) ->
  116:     basic_test().
  117: 
  118: on_load(suite) ->
  119:     [];
  120: on_load(doc) ->
  121:     ["Tests profiling of unloaded module"];
  122: on_load(Config) when is_list(Config) ->
  123:     on_load_test(Config).
  124: 
  125: modules(suite) ->
  126:     [];
  127: modules(doc) ->
  128:     ["Tests profiling of several modules"];
  129: modules(Config) when is_list(Config) ->
  130:     modules_test(Config).
  131: 
  132: -endif. %-ifdef(STANDALONE). ... -else.
  133: 
  134: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  135: %%% The Tests
  136: %%%
  137: 
  138: basic_test() ->
  139:     ?line M = 1000,
  140:     %%
  141:     ?line M2 = M*2,
  142:     ?line M3 = M*3,
  143:     ?line M2__1 = M2 + 1,
  144:     ?line M3__1 = M3 + 1,
  145:     ?line N = cprof:stop(),
  146:     %%
  147:     ?line 2 = cprof:start(?MODULE, seq_r),
  148:     ?line 1 = cprof:start(?MODULE, seq, 3),
  149:     ?line L = seq(1, M, fun succ/1),
  150:     ?line Lr = seq_r(1, M, fun succ/1),
  151:     ?line L = lists:reverse(Lr),
  152:     %%
  153:     ?line io:format("~p~n~p~n~p~n", 
  154: 		    [erlang:trace_info({?MODULE,sec_r,3}, all),
  155: 		     erlang:trace_info({?MODULE,sec_r,4}, all),
  156: 		     erlang:trace_info({?MODULE,sec,3}, all)]),
  157:     %%
  158:     ?line ModAna1 = {?MODULE,M2__1,[{{?MODULE,seq_r,4},M},
  159: 				   {{?MODULE,seq,3},M},
  160: 				   {{?MODULE,seq_r,3},1}]},
  161:     ?line ModAna1 = cprof:analyse(?MODULE,0),
  162:     ?line {M2__1, [ModAna1]} = cprof:analyse(),
  163:     ?line ModAna1 = cprof:analyse(?MODULE, 1),
  164:     ?line {M2__1, [ModAna1]} = cprof:analyse(1),
  165:     %%
  166:     ?line ModAna2 = {?MODULE,M2__1,[{{?MODULE,seq_r,4},M},
  167: 				   {{?MODULE,seq,3},M}]},
  168:     ?line ModAna2 = cprof:analyse(?MODULE, 2),
  169:     ?line {M2__1, [ModAna2]} = cprof:analyse(2),
  170:     %%
  171:     2 = cprof:pause(?MODULE, seq_r),
  172:     ?line L = seq(1, M, fun succ/1),
  173:     ?line Lr = seq_r(1, M, fun succ/1),
  174:     %%
  175:     ?line ModAna3 = {?MODULE,M3__1,[{{?MODULE,seq,3},M2},
  176: 				   {{?MODULE,seq_r,4},M},
  177: 				   {{?MODULE,seq_r,3},1}]},
  178:     ?line ModAna3 = cprof:analyse(?MODULE),
  179:     %%
  180:     ?line N = cprof:pause(),
  181:     ?line L = seq(1, M, fun succ/1),
  182:     ?line Lr = seq_r(1, M, fun succ/1),
  183:     %%
  184:     ?line {M3__1, [ModAna3]} = cprof:analyse(),
  185:     %%
  186:     ?line N = cprof:restart(),
  187:     ?line L = seq(1, M, fun succ/1),
  188:     ?line Lr = seq_r(1, M, fun succ/1),
  189:     %%
  190:     ?line ModAna1 = cprof:analyse(?MODULE),
  191:     %%
  192:     ?line N = cprof:stop(),
  193:     ?line {?MODULE,0,[]} = cprof:analyse(?MODULE),
  194:     ?line {0,[]} = cprof:analyse(),
  195:     ok.
  196: 
  197: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  198: 
  199: on_load_test(Config) ->
  200:     ?line Priv = ?config(priv_dir, Config),
  201:     ?line Data = ?config(data_dir, Config),
  202:     ?line File = filename:join(Data, "cprof_SUITE_test"),
  203:     ?line Module = cprof_SUITE_test,
  204:     ?line M = 1000,
  205:     %%
  206:     ?line M2 = M*2,
  207:     ?line M2__1 = M2 + 1,
  208:     ?line N1 = cprof:start(),
  209: 
  210:     ?line {ok,Module} = c:c(File, [{outdir,Priv}]),
  211: 
  212:     %% If this system is hipe-enabled, the loader may have called module_info/1
  213:     %% when Module was loaded above. Reset the call count to avoid seeing
  214:     %% the call in the analysis below.
  215: 
  216:     ?line 1 = cprof:restart(Module, module_info, 1),
  217: 
  218:     ?line L = Module:seq(1, M, fun succ/1),
  219:     ?line Lr = Module:seq_r(1, M, fun succ/1),
  220:     ?line Lr = lists:reverse(L),
  221:     ?line N2 = cprof:pause(),
  222:     ?line N3 = cprof:pause(Module),
  223:     ?line {Module,M2__1,[{{Module,seq_r,4},M},
  224: 			 {{Module,seq,3},M},
  225: 			 {{Module,seq_r,3},1}]} = cprof:analyse(Module),
  226:     ?line io:format("~p ~p ~p~n", [N1, N2, N3]),
  227:     ?line code:purge(Module),
  228:     ?line code:delete(Module),
  229:     ?line N4 = N2 - N3,
  230:     %%
  231:     ?line N4 = cprof:restart(),
  232:     ?line {ok,Module} = c:c(File, [{outdir,Priv}]),
  233:     ?line L = Module:seq(1, M, fun succ/1),
  234:     ?line Lr = Module:seq_r(1, M, fun succ/1),
  235:     ?line L = seq(1, M, fun succ/1),
  236:     ?line Lr = seq_r(1, M, fun succ/1),
  237:     ?line N2 = cprof:pause(),
  238:     ?line {Module,0,[]} = cprof:analyse(Module),
  239:     ?line M_1 = M - 1,
  240:     ?line M4__4 = M*4 - 4,
  241:     ?line M10_7 = M*10 - 7,
  242:     ?line {?MODULE,M10_7,[{{?MODULE,succ,1},M4__4},
  243: 			  {{?MODULE,seq_r,4},M},
  244: 			  {{?MODULE,seq,3},M},
  245: 			  {{?MODULE,'-on_load_test/1-fun-5-',1},M_1},
  246: 			  {{?MODULE,'-on_load_test/1-fun-4-',1},M_1},
  247: 			  {{?MODULE,'-on_load_test/1-fun-3-',1},M_1},
  248: 			  {{?MODULE,'-on_load_test/1-fun-2-',1},M_1},
  249: 			  {{?MODULE,seq_r,3},1}]} 
  250: 	= cprof:analyse(?MODULE),
  251:     ?line N2 = cprof:stop(),
  252:     ok.
  253: 
  254: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  255: 
  256: modules_test(Config) ->
  257:     ?line Priv = ?config(priv_dir, Config),
  258:     ?line Data = ?config(data_dir, Config),
  259:     ?line File = filename:join(Data, "cprof_SUITE_test"),
  260:     ?line Module = cprof_SUITE_test,
  261:     ?line {ok,Module} = c:c(File, [{outdir,Priv}]),
  262:     ?line M = 10,
  263:     %%
  264:     ?line M2 = M*2,
  265:     ?line M2__1 = M2 + 1,
  266:     ?line erlang:yield(),
  267:     ?line N = cprof:start(),
  268:     ?line L = Module:seq(1, M, fun succ/1),
  269:     ?line Lr = Module:seq_r(1, M, fun succ/1),
  270:     ?line L = seq(1, M, fun succ/1),
  271:     ?line Lr = seq_r(1, M, fun succ/1),
  272:     ?line N = cprof:pause(),
  273:     ?line Lr = lists:reverse(L),
  274:     ?line M_1 = M - 1,
  275:     ?line M4_4 = M*4 - 4,
  276:     ?line M10_7 = M*10 - 7,
  277:     ?line M2__1 = M*2 + 1,
  278:     ?line {Tot,ModList} = cprof:analyse(),
  279:     ?line {value,{?MODULE,M10_7,[{{?MODULE,succ,1},M4_4},
  280: 				 {{?MODULE,seq_r,4},M},
  281: 				 {{?MODULE,seq,3},M},
  282: 				 {{?MODULE,'-modules_test/1-fun-3-',1},M_1},
  283: 				 {{?MODULE,'-modules_test/1-fun-2-',1},M_1},
  284: 				 {{?MODULE,'-modules_test/1-fun-1-',1},M_1},
  285: 				 {{?MODULE,'-modules_test/1-fun-0-',1},M_1},
  286: 				 {{?MODULE,seq_r,3},1}]}} =
  287: 	lists:keysearch(?MODULE, 1, ModList),
  288:     ?line {value,{Module,M2__1,[{{Module,seq_r,4},M},
  289: 				{{Module,seq,3},M},
  290: 				{{Module,seq_r,3},1}]}} = 
  291: 	lists:keysearch(Module, 1, ModList),
  292:     ?line Tot = lists:foldl(fun ({_,C,_}, A) -> C+A end, 0, ModList),
  293:     ?line {cprof,_,Prof} = cprof:analyse(cprof),
  294:     ?line {value,{{cprof,pause,0},1}} = 
  295: 	lists:keysearch({cprof,pause,0}, 1, Prof),
  296:     ?line N = cprof:stop(),
  297:     ok.
  298: 
  299: 
  300: 
  301: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  302: %% Local helpers
  303: 
  304: 
  305: 
  306: %% Stack recursive seq
  307: seq(Stop, Stop, Succ) when is_function(Succ) ->
  308:     [Stop];
  309: seq(Start, Stop, Succ) when is_function(Succ) ->
  310:     [Start | seq(Succ(Start), Stop, Succ)].
  311: 
  312: 
  313: 
  314: %% Tail recursive seq, result list is reversed
  315: seq_r(Start, Stop, Succ) when is_function(Succ) ->
  316:     seq_r(Start, Stop, Succ, []).
  317: 
  318: seq_r(Stop, Stop, _, R) ->
  319:     [Stop | R];
  320: seq_r(Start, Stop, Succ, R) ->
  321:     seq_r(Succ(Start), Stop, Succ, [Start | R]).
  322: 
  323: 
  324: 
  325: %% Successor
  326: succ(X) -> X+1.