1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2011. 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(trace_call_time_SUITE). 32: 33: %% Exported end user tests 34: 35: -export([seq/3, seq_r/3]). 36: -export([loaded/1, a_function/1, a_called_function/1, dec/1, nif_dec/1]). 37: 38: -define(US_ERROR, 10000). 39: -define(R_ERROR, 0.8). 40: -define(SINGLE_CALL_US_TIME, 10). 41: 42: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 43: %% Result examination macros 44: 45: -define(CT(P,MFA),{trace,P,call,MFA}). 46: -define(CTT(P, MFA),{trace_ts,P,call,MFA,{_,_,_}}). 47: -define(RF(P,MFA,V),{trace,P,return_from,MFA,V}). 48: -define(RFT(P,MFA,V),{trace_ts,P,return_from,MFA,V,{_,_,_}}). 49: -define(RT(P,MFA),{trace,P,return_to,MFA}). 50: -define(RTT(P,MFA),{trace_ts,P,return_to,MFA,{_,_,_}}). 51: 52: -ifdef(debug). 53: -define(dbgformat(A,B),io:format(A,B)). 54: -else. 55: -define(dbgformat(A,B),noop). 56: -endif. 57: 58: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 59: 60: -include_lib("test_server/include/test_server.hrl"). 61: 62: %% When run in test server. 63: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 64: init_per_group/2,end_per_group/2, 65: init_per_testcase/2, end_per_testcase/2, not_run/1]). 66: -export([basic/1, on_and_off/1, info/1, 67: pause_and_restart/1, scheduling/1, called_function/1, combo/1, 68: bif/1, nif/1]). 69: 70: init_per_testcase(_Case, Config) -> 71: ?line Dog=test_server:timetrap(test_server:seconds(400)), 72: erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time,call_count]), 73: erlang:trace_pattern(on_load, false, [local,meta,call_time,call_count]), 74: timer:now_diff(now(),now()), 75: [{watchdog, Dog}|Config]. 76: 77: end_per_testcase(_Case, Config) -> 78: erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time,call_count]), 79: erlang:trace_pattern(on_load, false, [local,meta,call_time,call_count]), 80: erlang:trace(all, false, [all]), 81: Dog=?config(watchdog, Config), 82: test_server:timetrap_cancel(Dog), 83: ok. 84: 85: suite() -> [{ct_hooks,[ts_install_cth]}]. 86: 87: all() -> 88: case test_server:is_native(trace_call_time_SUITE) of 89: true -> [not_run]; 90: false -> 91: [basic, on_and_off, info, pause_and_restart, scheduling, 92: combo, bif, nif, called_function] 93: end. 94: 95: groups() -> 96: []. 97: 98: init_per_suite(Config) -> 99: Config. 100: 101: end_per_suite(_Config) -> 102: ok. 103: 104: init_per_group(_GroupName, Config) -> 105: Config. 106: 107: end_per_group(_GroupName, Config) -> 108: Config. 109: 110: 111: not_run(Config) when is_list(Config) -> 112: {skipped,"Native code"}. 113: 114: basic(suite) -> 115: []; 116: basic(doc) -> 117: ["Tests basic call count trace"]; 118: basic(Config) when is_list(Config) -> 119: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 120: ?line M = 1000, 121: %% 122: ?line 1 = erlang:trace_pattern({?MODULE,seq, '_'}, true, [call_time]), 123: ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]), 124: ?line Pid = setup(), 125: ?line {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> (X+1) end) end), 126: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), 127: ?line ok = check_trace_info({?MODULE, seq_r, 3}, [], none), 128: 129: ?line {Lr, T2} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> (X+1) end) end), 130: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), 131: ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Pid, 1, 0, 0}], T2/M), 132: ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid, M, 0, 0}], T2), 133: ?line L = lists:reverse(Lr), 134: 135: %% 136: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 137: ?line Pid ! quit, 138: ok. 139: 140: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 141: 142: 143: on_and_off(suite) -> 144: []; 145: on_and_off(doc) -> 146: ["Tests turning trace parameters on and off"]; 147: on_and_off(Config) when is_list(Config) -> 148: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 149: ?line M = 100, 150: %% 151: ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]), 152: ?line Pid = setup(), 153: ?line {L, T1} = execute(Pid, {?MODULE, seq, [1, M, fun(X) -> X+1 end]}), 154: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T1), 155: 156: ?line N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_time]), 157: ?line {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), 158: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T2), 159: 160: ?line P = erlang:trace_pattern({'_','_','_'}, true, [call_time]), 161: ?line {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), 162: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid, M, 0, 0}], T3), 163: 164: ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]), 165: ?line ok = check_trace_info({?MODULE, seq, 3}, false, none), 166: ?line {L, _T4} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), 167: ?line ok = check_trace_info({?MODULE, seq, 3}, false, none), 168: ?line ok = check_trace_info({?MODULE, seq_r, 4}, [], none), 169: ?line {Lr, T5} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end), 170: ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Pid,M,0,0}], T5), 171: 172: ?line N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_time]), 173: ?line ok = check_trace_info({?MODULE, seq_r, 4}, false, none), 174: ?line {Lr, _T6} = execute(Pid, fun() -> seq_r(1, M, fun(X) -> X+1 end) end), 175: ?line ok = check_trace_info({?MODULE, seq_r, 4}, false, none), 176: ?line L = lists:reverse(Lr), 177: %% 178: ?line Pid ! quit, 179: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 180: ok. 181: 182: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 183: 184: info(suite) -> 185: []; 186: info(doc) -> 187: ["Tests the trace_info BIF"]; 188: info(Config) when is_list(Config) -> 189: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 190: %% 191: ?line 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_time]), 192: ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), 193: ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]), 194: ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), 195: ?line {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all), 196: ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, L), 197: ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]), 198: ?line {call_time,[]} = erlang:trace_info({?MODULE,seq,3}, call_time), 199: ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_time]), 200: ?line {call_time,false} = erlang:trace_info({?MODULE,seq,3}, call_time), 201: ?line {all,false} = erlang:trace_info({?MODULE,seq,3}, all), 202: %% 203: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 204: ok. 205: 206: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 207: 208: pause_and_restart(suite) -> 209: []; 210: pause_and_restart(doc) -> 211: ["Tests pausing and restarting call time counters"]; 212: pause_and_restart(Config) when is_list(Config) -> 213: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 214: ?line M = 100, 215: ?line Pid = setup(), 216: %% 217: ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_time]), 218: ?line ok = check_trace_info({?MODULE, seq, 3}, [], none), 219: ?line {L, T1} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), 220: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1), 221: ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_time]), 222: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T1), 223: ?line {L, T2} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), 224: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T2), 225: ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_time]), 226: ?line ok = check_trace_info({?MODULE, seq, 3}, [], none), 227: ?line {L, T3} = execute(Pid, fun() -> seq(1, M, fun(X) -> X+1 end) end), 228: ?line ok = check_trace_info({?MODULE, seq, 3}, [{Pid,M,0,0}], T3), 229: %% 230: ?line Pid ! quit, 231: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 232: ok. 233: 234: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 235: 236: scheduling(suite) -> 237: []; 238: scheduling(doc) -> 239: ["Tests in/out scheduling of call time counters"]; 240: scheduling(Config) when is_list(Config) -> 241: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 242: ?line M = 1000000, 243: ?line Np = erlang:system_info(schedulers_online), 244: ?line F = 12, 245: 246: %% setup load processes 247: %% (single, no internal calls) 248: 249: ?line erlang:trace_pattern({?MODULE,loaded,1}, true, [call_time]), 250: 251: ?line Pids = [setup() || _ <- lists:seq(1, F*Np)], 252: ?line {_Ls,T1} = execute(Pids, {?MODULE,loaded,[M]}), 253: ?line [Pid ! quit || Pid <- Pids], 254: 255: %% logic dictates that each process will get ~ 1/F of the schedulers time 256: 257: ?line {call_time, CT} = erlang:trace_info({?MODULE,loaded,1}, call_time), 258: 259: ?line lists:foreach(fun (Pid) -> 260: ?line ok = case check_process_time(lists:keysearch(Pid, 1, CT), M, F, T1) of 261: schedule_time_error -> 262: test_server:comment("Warning: Failed time ratio"), 263: ok; 264: Other -> Other 265: end 266: end, Pids), 267: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 268: ok. 269: 270: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 271: 272: combo(suite) -> 273: []; 274: combo(doc) -> 275: ["Tests combining local call trace and meta trace with call time trace"]; 276: combo(Config) when is_list(Config) -> 277: ?line Self = self(), 278: ?line Nbc = 3, 279: ?line MetaMs = [{'_',[],[{return_trace}]}], 280: ?line Flags = lists:sort([call, return_to]), 281: ?line LocalTracer = spawn_link(fun () -> relay_n(5 + Nbc + 3, Self) end), 282: ?line MetaTracer = spawn_link(fun () -> relay_n(9 + Nbc + 3, Self) end), 283: ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]), 284: ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_time]), 285: ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, MetaMs, [{meta,MetaTracer}]), 286: ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]), 287: 288: % bifs 289: ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]), 290: ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), 291: ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]), 292: %% not implemented 293: %?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]), 294: 295: ?line 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]), 296: %% 297: ?line {traced,local} = 298: erlang:trace_info({?MODULE,seq_r,3}, traced), 299: ?line {match_spec,[]} = 300: erlang:trace_info({?MODULE,seq_r,3}, match_spec), 301: ?line {meta,MetaTracer} = 302: erlang:trace_info({?MODULE,seq_r,3}, meta), 303: ?line {meta_match_spec,MetaMs} = 304: erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec), 305: ?line ok = check_trace_info({?MODULE, seq_r, 3}, [], none), 306: 307: %% check empty trace_info for ?MODULE:seq_r/3 308: ?line {all,[_|_]=TraceInfo} = erlang:trace_info({?MODULE,seq_r,3}, all), 309: ?line {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo), 310: ?line {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfo), 311: ?line {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo), 312: ?line {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfo), 313: ?line {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfo), 314: ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfo), 315: 316: %% check empty trace_info for erlang:term_to_binary/1 317: ?line {all, [_|_] = TraceInfoBif} = erlang:trace_info({erlang, term_to_binary, 1}, all), 318: ?line {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfoBif), 319: ?line {value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif), 320: ?line {value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif), 321: ?line {value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif), 322: %% not implemented 323: ?line {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif), 324: %?line {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif), 325: ?line {value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif), 326: 327: %% 328: ?line [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end), 329: ?line T0 = now(), 330: ?line with_bif(Nbc), 331: ?line T1 = now(), 332: ?line TimeB = timer:now_diff(T1,T0), 333: %% 334: 335: ?line List = collect(100), 336: ?line {MetaR, LocalR} = 337: lists:foldl( 338: fun ({P,X}, {M,L}) when P == MetaTracer -> 339: {[X|M],L}; 340: ({P,X}, {M,L}) when P == LocalTracer -> 341: {M,[X|L]} 342: end, 343: {[],[]}, 344: List), 345: ?line Meta = lists:reverse(MetaR), 346: ?line Local = lists:reverse(LocalR), 347: 348: ?line [?CTT(Self,{?MODULE,seq_r,[1,3,_]}), 349: ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}), 350: ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), 351: ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), 352: ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), 353: ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), 354: ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]), 355: ?RFT(Self,{?MODULE,seq_r,3},[3,2,1]), 356: ?CTT(Self,{erlang,term_to_binary,[3]}), % bif 357: ?RFT(Self,{erlang,term_to_binary,1},<<131,97,3>>), 358: ?CTT(Self,{erlang,term_to_binary,[2]}), 359: ?RFT(Self,{erlang,term_to_binary,1},<<131,97,2>>) 360: ] = Meta, 361: 362: ?line [?CT(Self,{?MODULE,seq_r,[1,3,_]}), 363: ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}), 364: ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}), 365: ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}), 366: ?RT(Self,{?MODULE,combo,1}), 367: ?CT(Self,{erlang,term_to_binary,[3]}), % bif 368: ?RT(Self,{?MODULE,with_bif,1}), 369: ?CT(Self,{erlang,term_to_binary,[2]}), 370: ?RT(Self,{?MODULE,with_bif,1}) 371: ] = Local, 372: 373: ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1), 374: ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1), 375: ?line ok = check_trace_info({?MODULE, seq_r, 3}, [{Self,1,0,0}], 1), 376: ?line ok = check_trace_info({?MODULE, seq_r, 4}, [{Self,3,0,0}], 1), 377: ?line ok = check_trace_info({erlang, term_to_binary, 1}, [{self(), Nbc - 1, 0, 0}], TimeB), 378: %% 379: ?line erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_time]), 380: ?line erlang:trace_pattern(on_load, false, [local,meta,call_time]), 381: ?line erlang:trace(all, false, [all]), 382: ok. 383: 384: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 385: 386: bif(suite) -> 387: []; 388: bif(doc) -> 389: ["Tests tracing of bifs"]; 390: bif(Config) when is_list(Config) -> 391: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 392: ?line M = 1000000, 393: %% 394: ?line 2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]), 395: ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), 396: ?line Pid = setup(), 397: ?line {L, T1} = execute(Pid, fun() -> with_bif(M) end), 398: 399: ?line ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M - 1, 0, 0}], T1/2), 400: ?line ok = check_trace_info({erlang, term_to_binary, 1}, [{Pid, M - 1, 0, 0}], T1/2), 401: 402: % disable term2binary 403: 404: ?line 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, false, [call_time]), 405: 406: ?line {L, T2} = execute(Pid, fun() -> with_bif(M) end), 407: 408: ?line ok = check_trace_info({erlang, binary_to_term, 1}, [{Pid, M*2 - 2, 0, 0}], T1/2 + T2), 409: ?line ok = check_trace_info({erlang, term_to_binary, 1}, false, none), 410: 411: %% 412: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 413: ?line Pid ! quit, 414: ok. 415: 416: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 417: 418: nif(suite) -> 419: []; 420: nif(doc) -> 421: ["Tests tracing of nifs"]; 422: nif(Config) when is_list(Config) -> 423: load_nif(Config), 424: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 425: ?line M = 1000000, 426: %% 427: ?line 1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]), 428: ?line 1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]), 429: ?line Pid = setup(), 430: ?line {_, T1} = execute(Pid, fun() -> with_nif(M) end), 431: 432: % the nif is called M - 1 times, the last time the function with 'with_nif' 433: % returns ok and does not call the nif. 434: ?line ok = check_trace_info({?MODULE, nif_dec, 1}, [{Pid, M-1, 0, 0}], T1/5*4), 435: ?line ok = check_trace_info({?MODULE, with_nif, 1}, [{Pid, M, 0, 0}], T1/5), 436: 437: %% 438: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 439: ?line Pid ! quit, 440: ok. 441: 442: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 443: 444: called_function(suite) -> 445: []; 446: called_function(doc) -> 447: ["Tests combining nested function calls and that the time accumulates to the right function"]; 448: called_function(Config) when is_list(Config) -> 449: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 450: ?line M = 2100, 451: ?line Pid = setup(), 452: %% 453: ?line 1 = erlang:trace_pattern({?MODULE,a_function,'_'}, true, [call_time]), 454: ?line {L, T1} = execute(Pid, {?MODULE, a_function, [M]}), 455: ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M, 0, 0}], T1), 456: 457: ?line 1 = erlang:trace_pattern({?MODULE,a_called_function,'_'}, true, [call_time]), 458: ?line {L, T2} = execute(Pid, {?MODULE, a_function, [M]}), 459: ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M, 0, 0}], T1 + M*?SINGLE_CALL_US_TIME), 460: ?line ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M, 0, 0}], T2), 461: 462: 463: ?line 1 = erlang:trace_pattern({?MODULE,dec,'_'}, true, [call_time]), 464: ?line {L, T3} = execute(Pid, {?MODULE, a_function, [M]}), 465: ?line ok = check_trace_info({?MODULE, a_function, 1}, [{Pid, M+M+M, 0, 0}], T1 + (M+M)*?SINGLE_CALL_US_TIME), 466: ?line ok = check_trace_info({?MODULE, a_called_function, 1}, [{Pid, M+M, 0, 0}], T2 + M*?SINGLE_CALL_US_TIME ), 467: ?line ok = check_trace_info({?MODULE, dec, 1}, [{Pid, M, 0, 0}], T3), 468: 469: ?line Pid ! quit, 470: ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), 471: ok. 472: 473: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 474: %%% The Tests 475: %%% 476: 477: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 478: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 479: %% Local helpers 480: 481: 482: load_nif(Config) -> 483: ?line Path = ?config(data_dir, Config), 484: ?line ok = erlang:load_nif(filename:join(Path,"trace_nif"), 0). 485: 486: 487: %% Stack recursive seq 488: seq(Stop, Stop, Succ) when is_function(Succ) -> 489: [Stop]; 490: seq(Start, Stop, Succ) when is_function(Succ) -> 491: [Start | seq(Succ(Start), Stop, Succ)]. 492: 493: 494: a_function(1) -> a_called_function(1); 495: a_function(N) when N > 1 -> a_function(a_called_function(N)). 496: 497: a_called_function(N) -> dec(N). 498: 499: with_bif(1) -> ok; 500: with_bif(N) -> 501: with_bif(erlang:binary_to_term(erlang:term_to_binary(N)) - 1). 502: 503: with_nif(0) -> error; 504: with_nif(1) -> ok; 505: with_nif(N) -> 506: with_nif(?MODULE:nif_dec(N)). 507: 508: 509: nif_dec(_) -> 0. 510: 511: dec(N) -> 512: loaded(10000), 513: N - 1. 514: 515: loaded(N) when N > 1 -> loaded(N - 1); 516: loaded(_) -> 5. 517: 518: 519: %% Tail recursive seq, result list is reversed 520: seq_r(Start, Stop, Succ) when is_function(Succ) -> 521: seq_r(Start, Stop, Succ, []). 522: 523: seq_r(Stop, Stop, _, R) -> 524: [Stop | R]; 525: seq_r(Start, Stop, Succ, R) -> 526: seq_r(Succ(Start), Stop, Succ, [Start | R]). 527: 528: % Check call time tracing data and print mismatches 529: check_trace_info(Mfa, [{Pid, C,_,_}] = Expect, Time) -> 530: case erlang:trace_info(Mfa, call_time) of 531: % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME) 532: % is the same. 533: % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok. 534: {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR -> 535: ok; 536: {call_time,[{Pid,C,S,Us}]} -> 537: Sum = S*1000000 + Us, 538: io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n", 539: [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]), 540: time_error; 541: Other -> 542: io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]), 543: time_count_error 544: end; 545: check_trace_info(Mfa, Expect, _) -> 546: case erlang:trace_info(Mfa, call_time) of 547: {call_time, Expect} -> 548: ok; 549: Other -> 550: io:format("Expected ~p -> {call_time, ~p}~n - got ~p~n", [Mfa, Expect, Other]), 551: result_not_expected_error 552: end. 553: 554: 555: %check process time 556: check_process_time({value,{Pid, M, S, Us}}, M, F, Time) -> 557: ?line Sum = S*1000000 + Us, 558: if 559: abs(1 - (F/(Time/Sum))) < ?R_ERROR -> 560: ok; 561: true -> 562: io:format("- Pid ~p, Got ratio ~.2f, expected ratio ~w~n", [Pid, Time/Sum,F]), 563: schedule_time_error 564: end; 565: check_process_time(Other, M, _, _) -> 566: io:format(" - Got ~p, expected count ~w~n", [Other, M]), 567: error. 568: 569: 570: 571: %% Message relay process 572: relay_n(0, _) -> 573: ok; 574: relay_n(N, Dest) -> 575: receive Msg -> 576: Dest ! {self(), Msg}, 577: relay_n(N-1, Dest) 578: end. 579: 580: 581: 582: %% Collect received messages 583: collect(Time) -> 584: Ref = erlang:start_timer(Time, self(), done), 585: L = lists:reverse(collect([], Ref)), 586: ?dbgformat("Got: ~p~n",[L]), 587: L. 588: 589: collect(A, 0) -> 590: receive 591: Mess -> 592: collect([Mess | A], 0) 593: after 0 -> 594: A 595: end; 596: collect(A, Ref) -> 597: receive 598: {timeout, Ref, done} -> 599: collect(A, 0); 600: Mess -> 601: collect([Mess | A], Ref) 602: end. 603: 604: setup() -> 605: Pid = spawn_link(fun() -> loop() end), 606: ?line 1 = erlang:trace(Pid, true, [call]), 607: Pid. 608: 609: execute(Pids, Mfa) when is_list(Pids) -> 610: T0 = now(), 611: [P ! {self(), execute, Mfa} || P <- Pids], 612: As = [receive {P, answer, Answer} -> Answer end || P <- Pids], 613: T1 = now(), 614: {As, timer:now_diff(T1,T0)}; 615: execute(P, Mfa) -> 616: T0 = now(), 617: P ! {self(), execute, Mfa}, 618: A = receive {P, answer, Answer} -> Answer end, 619: T1 = now(), 620: {A, timer:now_diff(T1,T0)}. 621: 622: 623: 624: loop() -> 625: receive 626: quit -> 627: ok; 628: {Pid, execute, Fun } when is_function(Fun) -> 629: Pid ! {self(), answer, erlang:apply(Fun, [])}, 630: loop(); 631: {Pid, execute, {M, F, A}} -> 632: Pid ! {self(), answer, erlang:apply(M, F, A)}, 633: loop() 634: end.