1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2001-2012. 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: -module(fprof_SUITE). 20: 21: -include_lib("test_server/include/test_server.hrl"). 22: 23: %% Test server framework exports 24: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 25: init_per_group/2,end_per_group/2, not_run/1]). 26: 27: %% Test suites 28: -export([stack_seq/1, tail_seq/1, create_file_slow/1, spawn_simple/1, 29: imm_tail_seq/1, imm_create_file_slow/1, imm_compile/1, 30: cpu_create_file_slow/1]). 31: 32: %% Other exports 33: -export([create_file_slow/2]). 34: 35: 36: %% Debug exports 37: -export([parse/1, verify/2]). 38: -export([spawn_simple_test/3]). 39: 40: 41: -define(line_trace,true). 42: 43: %-define(debug,true). 44: -ifdef(debug). 45: -define(dbg(Str,Args), io:format(Str,Args)). 46: -else. 47: -define(dbg(Str,Args), ok). 48: -endif. 49: 50: 51: 52: %%%--------------------------------------------------------------------- 53: %%% Test suites 54: %%%--------------------------------------------------------------------- 55: 56: 57: 58: suite() -> [{ct_hooks,[ts_install_cth]}]. 59: 60: all() -> 61: case test_server:is_native(fprof_SUITE) of 62: true -> [not_run]; 63: false -> 64: [stack_seq, tail_seq, create_file_slow, spawn_simple, 65: imm_tail_seq, imm_create_file_slow, imm_compile, 66: cpu_create_file_slow] 67: end. 68: 69: groups() -> 70: []. 71: 72: init_per_suite(Config) -> 73: Config. 74: 75: end_per_suite(_Config) -> 76: ok. 77: 78: init_per_group(_GroupName, Config) -> 79: Config. 80: 81: end_per_group(_GroupName, Config) -> 82: Config. 83: 84: 85: not_run(Config) when is_list(Config) -> 86: {skipped, "Native code"}. 87: 88: %%%--------------------------------------------------------------------- 89: 90: stack_seq(doc) -> 91: ["Tests a stack recursive variant of lists:seq/3"]; 92: stack_seq(suite) -> 93: []; 94: stack_seq(Config) when is_list(Config) -> 95: ?line Timetrap = ?t:timetrap(?t:seconds(20)), 96: ?line PrivDir = ?config(priv_dir, Config), 97: ?line TraceFile = 98: filename:join(PrivDir, ?MODULE_STRING"_stack_seq.trace"), 99: ?line AnalysisFile = 100: filename:join(PrivDir, ?MODULE_STRING"_stack_seq.analysis"), 101: ?line Start = 1, 102: ?line Stop = 1000, 103: ?line Succ = fun (X) -> X + 1 end, 104: ?line ok = fprof:stop(kill), 105: %% 106: ?line TS0 = erlang:now(), 107: ?line R0 = fprof:apply(fun seq/3, [Start, Stop, Succ], [{file, TraceFile}]), 108: ?line TS1 = erlang:now(), 109: ?line R = seq(Start, Stop, Succ), 110: ?line TS2 = erlang:now(), 111: ?line ok = fprof:profile(file, TraceFile), 112: ?line ok = fprof:analyse(), 113: ?line ok = fprof:analyse(dest, AnalysisFile), 114: ?line ok = fprof:stop(), 115: ?line R = R0, 116: %% 117: ?line {ok, [T, P]} = parse(AnalysisFile), 118: ?line io:format("~p~n~n~p~n", [P, ets:tab2list(T)]), 119: ?line ok = (catch verify(T, P)), 120: ?line Proc = pid_to_list(self()), 121: ?line case P of 122: [{analysis_options, _}, 123: [{totals, _, Acc, _}], 124: [{Proc, _, undefined, _} | _]] -> 125: ok 126: end, 127: %% 128: ?line check_own_and_acc(TraceFile,AnalysisFile), 129: %% 130: ?line ets:delete(T), 131: ?line file:delete(TraceFile), 132: ?line file:delete(AnalysisFile), 133: ?line ?t:timetrap_cancel(Timetrap), 134: ?line Acc1 = ts_sub(TS1, TS0), 135: ?line Acc2 = ts_sub(TS2, TS1), 136: ?line io:format("ts:~w, fprof:~w, bare:~w.~n", [Acc, Acc1, Acc2]), 137: {comment, io_lib:format("~p times slower", [Acc1/Acc2])}. 138: 139: %%%--------------------------------------------------------------------- 140: 141: tail_seq(doc) -> 142: ["Tests a tail recursive variant of lists:seq/3"]; 143: tail_seq(suite) -> 144: []; 145: tail_seq(Config) when is_list(Config) -> 146: ?line Timetrap = ?t:timetrap(?t:seconds(10)), 147: ?line PrivDir = ?config(priv_dir, Config), 148: ?line TraceFile = 149: filename:join(PrivDir, ?MODULE_STRING"_tail_seq.trace"), 150: ?line AnalysisFile = 151: filename:join(PrivDir, ?MODULE_STRING"_tail_seq.analysis"), 152: ?line Start = 1, 153: ?line Stop = 1000, 154: ?line Succ = fun (X) -> X + 1 end, 155: ?line ok = fprof:stop(kill), 156: %% 157: ?line TS0 = erlang:now(), 158: ?line R = seq_r(Start, Stop, Succ), 159: ?line TS1 = erlang:now(), 160: %% 161: ?line R1 = fprof:apply(fun seq_r/3, [Start, Stop, Succ], 162: [{file, TraceFile}]), 163: ?line TS2 = erlang:now(), 164: ?line ok = fprof:profile([{file,TraceFile}]), 165: ?line ok = fprof:analyse(), 166: ?line ok = fprof:analyse(dest, AnalysisFile), 167: ?line ok = fprof:stop(), 168: ?line R = R1, 169: %% 170: ?line {ok, [T, P]} = parse(AnalysisFile), 171: ?line io:format("~p~n~n~p~n", [P, ets:tab2list(T)]), 172: ?line ok = verify(T, P), 173: ?line Proc = pid_to_list(self()), 174: ?line case P of 175: [{analysis_options, _}, 176: [{totals, _, Acc, _}], 177: [{Proc, _, undefined, _} | _]] -> 178: ok 179: end, 180: %% 181: ?line check_own_and_acc(TraceFile,AnalysisFile), 182: %% 183: ?line ets:delete(T), 184: ?line file:delete(TraceFile), 185: ?line file:delete(AnalysisFile), 186: ?line ?t:timetrap_cancel(Timetrap), 187: ?line Acc1 = ts_sub(TS1, TS0), 188: ?line Acc2 = ts_sub(TS2, TS1), 189: ?line io:format("ts:~w, fprof:~w, bare:~w.~n", [Acc, Acc2, Acc1]), 190: {comment, io_lib:format("~p times slower", [Acc2/Acc1])}. 191: 192: %%%--------------------------------------------------------------------- 193: 194: %% Tests the create_file_slow benchmark. 195: create_file_slow(Config) -> 196: case test_server:is_native(lists) orelse 197: test_server:is_native(file) of 198: true -> 199: {skip,"Native libs -- tracing does not work"}; 200: false -> 201: do_create_file_slow(Config) 202: end. 203: 204: do_create_file_slow(Config) -> 205: ?line Timetrap = ?t:timetrap(?t:seconds(40)), 206: ?line PrivDir = ?config(priv_dir, Config), 207: ?line TraceFile = 208: filename:join(PrivDir, ?MODULE_STRING"_create_file_slow.trace"), 209: ?line AnalysisFile = 210: filename:join(PrivDir, ?MODULE_STRING"_create_file_slow.analysis"), 211: ?line DataFile = 212: filename:join(PrivDir, ?MODULE_STRING"_create_file_slow.data"), 213: ?line ok = fprof:stop(kill), 214: %% 215: ?line TS0 = erlang:now(), 216: ?line ok = create_file_slow(DataFile, 1024), 217: ?line TS1 = erlang:now(), 218: %% 219: ?line ok = file:delete(DataFile), 220: ?line TS2 = erlang:now(), 221: ?line ok = fprof:apply(?MODULE, create_file_slow, [DataFile, 1024], 222: [{file, TraceFile}]), 223: ?line TS3 = erlang:now(), 224: ?line ok = fprof:profile(file, TraceFile), 225: ?line ok = fprof:analyse(), 226: ?line ok = fprof:analyse(dest, AnalysisFile), 227: ?line ok = fprof:stop(), 228: %% 229: ?line {ok, [T, P]} = parse(AnalysisFile), 230: ?line io:format("~p~n~n~p~n", [P, ets:tab2list(T)]), 231: ?line ok = verify(T, P), 232: ?line Proc = pid_to_list(self()), 233: ?line case P of 234: [{analysis_options, _}, 235: [{totals, _, Acc, _}], 236: [{Proc, _, undefined, _} | _]] -> 237: ok 238: end, 239: %% 240: ?line check_own_and_acc(TraceFile,AnalysisFile), 241: %% 242: ?line ets:delete(T), 243: ?line file:delete(DataFile), 244: ?line file:delete(TraceFile), 245: ?line file:delete(AnalysisFile), 246: ?line ?t:timetrap_cancel(Timetrap), 247: ?line Acc1 = ts_sub(TS1, TS0), 248: ?line Acc3 = ts_sub(TS3, TS2), 249: ?line io:format("ts:~w, fprof:~w, bare:~w.~n", [Acc, Acc3, Acc1]), 250: {comment, io_lib:format("~p times slower", [Acc3/Acc1])}. 251: 252: 253: 254: %%%--------------------------------------------------------------------- 255: 256: spawn_simple(doc) -> 257: ["Tests process spawn"]; 258: spawn_simple(suite) -> 259: []; 260: spawn_simple(Config) when is_list(Config) -> 261: ?line Timetrap = ?t:timetrap(?t:seconds(30)), 262: ?line PrivDir = ?config(priv_dir, Config), 263: ?line TraceFile = 264: filename:join(PrivDir, ?MODULE_STRING"_spawn_simple.trace"), 265: ?line AnalysisFile = 266: filename:join(PrivDir, ?MODULE_STRING"_spawn_simple.analysis"), 267: ?line Start = 1, 268: ?line Stop = 1000, 269: ?line Succ = fun (X) -> X + 1 end, 270: ?line ok = fprof:stop(kill), 271: %% 272: ?line TS0 = erlang:now(), 273: ?line {{_, R1}, {_, R2}} = spawn_simple_test(Start, Stop, Succ), 274: ?line TS1 = erlang:now(), 275: %% 276: ?line ok = fprof:trace(start, TraceFile), 277: ?line {{P1, R3}, {P2, R4}} = spawn_simple_test(Start, Stop, Succ), 278: ?line ok = fprof:trace(stop), 279: ?line TS2 = erlang:now(), 280: ?line ok = fprof:profile(file, TraceFile), 281: ?line ok = fprof:analyse(), 282: ?line ok = fprof:analyse(dest, AnalysisFile), 283: ?line ok = fprof:stop(), 284: ?line R1 = R3, 285: ?line R2 = R4, 286: %% 287: ?line {ok, [T, P]} = parse(AnalysisFile), 288: ?line io:format("~p~n~n~p~n", [P, ets:tab2list(T)]), 289: ?line ok = verify(T, P), 290: ?line Proc1 = pid_to_list(P1), 291: ?line Proc2 = pid_to_list(P2), 292: ?line Proc0 = pid_to_list(self()), 293: ?line io:format("~p~n ~p ~p ~p~n", [P, Proc0, Proc1, Proc2]), 294: ?line [{analysis_options, _}, [{totals, _, Acc, _}] | Procs] = P, 295: ?line [[{Proc0, _, undefined, _} | _]] = 296: lists:filter(fun ([Pt | _]) when element(1, Pt) == Proc0 -> true; 297: (_) -> false 298: end, Procs), 299: ?line [[{Proc1, _, undefined, _}, 300: {spawned_by, Proc0}, 301: {spawned_as, {erlang, apply, ["#Fun"++_, []]}}, 302: {initial_calls, [{erlang, apply, 2}, 303: {?MODULE, '-spawn_simple_test/3-fun-0-', 4}]} 304: | _]] = 305: lists:filter(fun ([Pt | _]) when element(1, Pt) == Proc1 -> true; 306: (_) -> false 307: end, Procs), 308: ?line [[{Proc2, _, undefined, _}, 309: {spawned_by, Proc0}, 310: {spawned_as, {erlang, apply, ["#Fun"++_, []]}}, 311: {initial_calls, [{erlang, apply, 2}, 312: {?MODULE, '-spawn_simple_test/3-fun-1-', 4}]} 313: | _]] = 314: lists:filter(fun ([Pt | _]) when element(1, Pt) == Proc2 -> true; 315: (_) -> false 316: end, Procs), 317: ?line 3 = length(Procs), 318: ?line R1 = lists:reverse(R2), 319: %% 320: ?line check_own_and_acc(TraceFile,AnalysisFile), 321: %% 322: ?line ets:delete(T), 323: ?line file:delete(TraceFile), 324: ?line file:delete(AnalysisFile), 325: ?line ?t:timetrap_cancel(Timetrap), 326: ?line Acc1 = ts_sub(TS1, TS0), 327: ?line Acc2 = ts_sub(TS2, TS1), 328: ?line io:format("ts:~w, fprof:~w, bare:~w.~n", [Acc, Acc2, Acc1]), 329: {comment, io_lib:format("~p times slower", [Acc2/Acc1])}. 330: 331: 332: spawn_simple_test(Start, Stop, Succ) -> 333: Parent = self(), 334: Seq = 335: spawn_link( 336: fun () -> 337: Parent ! {self(), seq(Start, Stop, Succ)} 338: end), 339: SeqR = 340: spawn_link( 341: fun () -> 342: Parent ! {self(), seq_r(Start, Stop, Succ)} 343: end), 344: receive {Seq, SeqResult} -> 345: receive {SeqR, SeqRResult} -> 346: {{Seq, SeqResult}, {SeqR, SeqRResult}} 347: end 348: end. 349: 350: 351: 352: %%%--------------------------------------------------------------------- 353: 354: imm_tail_seq(doc) -> 355: ["Tests a tail recursive variant of lists:seq/3 ", 356: "with immediate trace to profile"]; 357: imm_tail_seq(suite) -> 358: []; 359: imm_tail_seq(Config) when is_list(Config) -> 360: ?line Timetrap = ?t:timetrap(?t:seconds(10)), 361: ?line PrivDir = ?config(priv_dir, Config), 362: ?line AnalysisFile = 363: filename:join(PrivDir, ?MODULE_STRING"_imm_tail_seq.analysis"), 364: ?line Start = 1, 365: ?line Stop = 1000, 366: ?line Succ = fun (X) -> X + 1 end, 367: ?line ok = fprof:stop(kill), 368: ?line catch eprof:stop(), 369: %% 370: ?line TS0 = erlang:now(), 371: ?line R0 = seq_r(Start, Stop, Succ), 372: ?line TS1 = erlang:now(), 373: %% 374: ?line profiling = eprof:start_profiling([self()]), 375: ?line TS2 = erlang:now(), 376: ?line R2 = seq_r(Start, Stop, Succ), 377: ?line TS3 = erlang:now(), 378: ?line profiling_stopped = eprof:stop_profiling(), 379: ?line R2 = R0, 380: %% 381: ?line eprof:analyze(), 382: ?line stopped = eprof:stop(), 383: %% 384: ?line {ok, Tracer} = fprof:profile(start), 385: ?line ok = fprof:trace([start, {tracer, Tracer}]), 386: ?line TS4 = erlang:now(), 387: ?line R4 = seq_r(Start, Stop, Succ), 388: ?line TS5 = erlang:now(), 389: ?line ok = fprof:trace(stop), 390: ?line ok = fprof:analyse(), 391: ?line ok = fprof:analyse(dest, AnalysisFile), 392: ?line ok = fprof:stop(), 393: ?line R4 = R0, 394: %% 395: ?line {ok, [T, P]} = parse(AnalysisFile), 396: ?line io:format("~p~n~n~p~n", [P, ets:tab2list(T)]), 397: ?line ok = verify(T, P), 398: ?line Proc = pid_to_list(self()), 399: ?line case P of 400: [{analysis_options, _}, 401: [{totals, _, Acc, _}], 402: [{Proc, _, undefined, _} | _]] -> 403: ok 404: end, 405: %% 406: ?line ets:delete(T), 407: ?line file:delete(AnalysisFile), 408: ?line ?t:timetrap_cancel(Timetrap), 409: ?line Acc1 = ts_sub(TS1, TS0), 410: ?line Acc3 = ts_sub(TS3, TS2), 411: ?line Acc5 = ts_sub(TS5, TS4), 412: ?line io:format("~p (plain), ~p (eprof), ~p (fprof), ~p (cpu)~n", 413: [Acc1/1000, Acc3/1000, Acc5/1000, Acc/1000]), 414: {comment, io_lib:format("~p/~p (fprof/eprof) times slower", 415: [Acc5/Acc1, Acc3/Acc1])}. 416: 417: %%%--------------------------------------------------------------------- 418: 419: imm_create_file_slow(doc) -> 420: ["Tests a tail recursive variant of lists:seq/3 ", 421: "with immediate trace to profile"]; 422: imm_create_file_slow(suite) -> 423: []; 424: imm_create_file_slow(Config) when is_list(Config) -> 425: ?line Timetrap = ?t:timetrap(?t:seconds(60)), 426: ?line PrivDir = ?config(priv_dir, Config), 427: ?line DataFile = 428: filename:join(PrivDir, ?MODULE_STRING"_imm_create_file_slow.data"), 429: ?line AnalysisFile = 430: filename:join(PrivDir, ?MODULE_STRING"_imm_create_file_slow.analysis"), 431: ?line ok = fprof:stop(kill), 432: %% 433: ?line TS0 = erlang:now(), 434: ?line ok = create_file_slow(DataFile, 1024), 435: ?line TS1 = erlang:now(), 436: ?line ok = file:delete(DataFile), 437: %% 438: ?line {ok, Tracer} = fprof:profile(start), 439: ?line TS2 = erlang:now(), 440: ?line ok = fprof:apply(?MODULE, create_file_slow, [DataFile, 1024], 441: [{tracer, Tracer}, continue]), 442: ?line TS3 = erlang:now(), 443: ?line ok = fprof:profile(stop), 444: ?line ok = fprof:analyse(), 445: ?line ok = fprof:analyse(dest, AnalysisFile), 446: ?line ok = fprof:stop(), 447: %% 448: ?line {ok, [T, P]} = parse(AnalysisFile), 449: ?line io:format("~p~n~n~p~n", [P, ets:tab2list(T)]), 450: ?line ok = verify(T, P), 451: ?line Proc = pid_to_list(self()), 452: ?line case P of 453: [{analysis_options, _}, 454: [{totals, _, Acc, _}], 455: [{Proc, _, undefined, _} | _]] -> 456: ok 457: end, 458: %% 459: ?line ets:delete(T), 460: ?line file:delete(DataFile), 461: ?line file:delete(AnalysisFile), 462: ?line ?t:timetrap_cancel(Timetrap), 463: ?line Acc1 = ts_sub(TS1, TS0), 464: ?line Acc3 = ts_sub(TS3, TS2), 465: ?line io:format("ts:~w, fprof:~w, bare:~w.~n", [Acc, Acc3, Acc1]), 466: {comment, io_lib:format("~p times slower", [Acc3/Acc1])}. 467: 468: %%%--------------------------------------------------------------------- 469: 470: imm_compile(doc) -> 471: ["Tests to compile a small source file ", 472: "with immediate trace to profile"]; 473: imm_compile(suite) -> 474: []; 475: imm_compile(Config) when is_list(Config) -> 476: ?line Timetrap = ?t:timetrap(?t:minutes(20)), 477: ?line DataDir = ?config(data_dir, Config), 478: ?line SourceFile = filename:join(DataDir, "foo.erl"), 479: ?line PrivDir = ?config(priv_dir, Config), 480: ?line AnalysisFile = 481: filename:join(PrivDir, ?MODULE_STRING"_imm_compile.analysis"), 482: ?line ok = fprof:stop(kill), 483: ?line catch eprof:stop(), 484: %% 485: ?line {ok, foo, _} = compile:file(SourceFile, [binary]), 486: ?line TS0 = erlang:now(), 487: ?line {ok, foo, _} = compile:file(SourceFile, [binary]), 488: ?line TS1 = erlang:now(), 489: %% 490: ?line profiling = eprof:start_profiling([self()]), 491: ?line TS2 = erlang:now(), 492: ?line {ok, foo, _} = compile:file(SourceFile, [binary]), 493: ?line TS3 = erlang:now(), 494: ?line profiling_stopped = eprof:stop_profiling(), 495: %% 496: ?line eprof:analyze(), 497: ?line stopped = eprof:stop(), 498: %% 499: ?line {ok, Tracer} = fprof:profile(start), 500: ?line ok = fprof:trace([start, {tracer, Tracer}]), 501: ?line TS4 = erlang:now(), 502: ?line {ok, foo, _} = compile:file(SourceFile, [binary]), 503: ?line TS5 = erlang:now(), 504: ?line ok = fprof:trace(stop), 505: %% 506: ?line io:format("Analysing...~n"), 507: ?line ok = fprof:analyse(dest, AnalysisFile), 508: ?line ok = fprof:stop(), 509: %% 510: ?line {ok, [T, P]} = parse(AnalysisFile), 511: ?line io:format("~p~n", [P]), 512: ?line Acc1 = ts_sub(TS1, TS0), 513: ?line Acc3 = ts_sub(TS3, TS2), 514: ?line Acc5 = ts_sub(TS5, TS4), 515: ?line io:format("Verifying...~n"), 516: ?line ok = verify(T, P), 517: ?line case P of 518: [{analysis_options, _}, 519: [{totals, _, Acc, _}] | _] -> 520: ok 521: end, 522: %% 523: ?line ets:delete(T), 524: ?line file:delete(AnalysisFile), 525: ?line ?t:timetrap_cancel(Timetrap), 526: ?line io:format("~p (plain), ~p (eprof), ~p (fprof), ~p(cpu)~n", 527: [Acc1/1000, Acc3/1000, Acc5/1000, Acc/1000]), 528: {comment, io_lib:format("~p/~p (fprof/eprof) times slower", 529: [Acc5/Acc1, Acc3/Acc1])}. 530: 531: %%%--------------------------------------------------------------------- 532: 533: cpu_create_file_slow(doc) -> 534: ["Tests the create_file_slow benchmark using cpu_time"]; 535: cpu_create_file_slow(suite) -> 536: []; 537: cpu_create_file_slow(Config) when is_list(Config) -> 538: ?line Timetrap = ?t:timetrap(?t:seconds(40)), 539: ?line PrivDir = ?config(priv_dir, Config), 540: ?line TraceFile = 541: filename:join(PrivDir, ?MODULE_STRING"_cpu_create_file_slow.trace"), 542: ?line AnalysisFile = 543: filename:join(PrivDir, ?MODULE_STRING"_cpu_create_file_slow.analysis"), 544: ?line DataFile = 545: filename:join(PrivDir, ?MODULE_STRING"_cpu_create_file_slow.data"), 546: ?line ok = fprof:stop(kill), 547: %% 548: ?line TS0 = erlang:now(), 549: ?line Result = (catch fprof:apply(?MODULE, create_file_slow, 550: [DataFile, 1024], 551: [{file, TraceFile}, cpu_time])), 552: ?line TS1 = erlang:now(), 553: ?line TestResult = 554: case Result of 555: ok -> 556: ?line ok = fprof:profile(file, TraceFile), 557: ?line ok = fprof:analyse(), 558: ?line ok = fprof:analyse(dest, AnalysisFile), 559: ?line ok = fprof:stop(), 560: %% 561: ?line {ok, [T, P]} = parse(AnalysisFile), 562: ?line io:format("~p~n~n~p~n", [P, ets:tab2list(T)]), 563: ?line ok = verify(T, P), 564: ?line Proc = pid_to_list(self()), 565: ?line case P of 566: [{analysis_options, _}, 567: [{totals, _, Acc, _}], 568: [{Proc, _, undefined, _} | _]] -> 569: ok 570: end, 571: %% 572: ?line check_own_and_acc(TraceFile,AnalysisFile), 573: %% 574: ?line ets:delete(T), 575: ?line file:delete(DataFile), 576: ?line file:delete(TraceFile), 577: ?line file:delete(AnalysisFile), 578: ?line Acc1 = ts_sub(TS1, TS0), 579: ?line io:format("cpu_ts:~w, fprof:~w~n", [Acc, Acc1]), 580: {comment, io_lib:format("~p% cpu utilization", 581: [100*Acc/Acc1])}; 582: {'EXIT', not_supported} -> 583: case {os:type(), os:version()} of 584: {{unix, sunos}, {Major, Minor, _}} 585: when Major >= 5, Minor >= 7 -> 586: test_server:fail(Result); 587: _ -> 588: {skipped, "not_supported"} 589: end; 590: _ -> 591: test_server:fail(Result) 592: end, 593: ?line ?t:timetrap_cancel(Timetrap), 594: TestResult. 595: 596: 597: 598: %%%--------------------------------------------------------------------- 599: %%% Functions to test 600: %%%--------------------------------------------------------------------- 601: 602: 603: 604: %% Stack recursive seq 605: seq(Stop, Stop, Succ) when is_function(Succ) -> 606: [Stop]; 607: seq(Start, Stop, Succ) when is_function(Succ) -> 608: [Start | seq(Succ(Start), Stop, Succ)]. 609: 610: 611: 612: %% Tail recursive seq, result list is reversed 613: seq_r(Start, Stop, Succ) when is_function(Succ) -> 614: seq_r(Start, Stop, Succ, []). 615: 616: seq_r(Stop, Stop, _, R) -> 617: [Stop | R]; 618: seq_r(Start, Stop, Succ, R) -> 619: seq_r(Succ(Start), Stop, Succ, [Start | R]). 620: 621: 622: 623: create_file_slow(Name, N) when is_integer(N), N >= 0 -> 624: {ok, FD} = 625: file:open(Name, [raw, write, delayed_write, binary]), 626: if N > 256 -> 627: ok = file:write(FD, 628: lists:map(fun (X) -> <<X:32/unsigned>> end, 629: lists:seq(0, 255))), 630: ok = create_file_slow(FD, 256, N); 631: true -> 632: ok = create_file_slow(FD, 0, N) 633: end, 634: ok = file:close(FD). 635: 636: create_file_slow(_FD, M, M) -> 637: ok; 638: create_file_slow(FD, M, N) -> 639: ok = file:write(FD, <<M:32/unsigned>>), 640: create_file_slow(FD, M+1, N). 641: 642: 643: 644: %%%--------------------------------------------------------------------- 645: %%% Profile verification functions 646: %%%--------------------------------------------------------------------- 647: 648: 649: 650: verify(Tab, [{analysis_options, _}, 651: [{totals, Cnt, Acc, Own} | _] | Processes]) -> 652: Processes_1 = 653: lists:map( 654: fun ([{Proc, Cnt_P, undefined, Own_P} | _]) -> 655: case sum_process(Tab, Proc) of 656: {Proc, Cnt_P, Acc_P, Own_P} = Clocks 657: when Acc_P >= Own_P -> 658: Clocks; 659: Weird -> 660: throw({error, [?MODULE, ?LINE, Weird]}) 661: end 662: end, 663: Processes), 664: case lists:foldl( 665: fun ({_, Cnt_P2, Acc_P2, Own_P2}, 666: {totals, Cnt_T, Acc_T, Own_T}) -> 667: {totals, Cnt_P2+Cnt_T, Acc_P2+Acc_T, Own_P2+Own_T} 668: end, 669: {totals, 0, 0, 0}, 670: Processes_1) of 671: {totals, Cnt, Acc_T, Own} when Acc_T >= Acc -> 672: ok; 673: Weird -> 674: throw({error, [?MODULE, ?LINE, Weird]}) 675: end. 676: 677: 678: 679: sum_process(Tab, Proc) -> 680: ets_select_fold( 681: Tab, [{{{Proc, '_'}, '_'}, [], ['$_']}], 100, 682: fun ({{P, MFA}, {Callers, {MFA, Cnt, Acc, Own}, Called}}, 683: {P, Cnt_P, Acc_P, Own_P}) when P == Proc -> 684: ok = verify_callers(Tab, Proc, MFA, Callers), 685: ok = verify_called(Tab, Proc, MFA, Called), 686: {P, Cnt+Cnt_P, Acc+Acc_P, Own+Own_P}; 687: (Weird, Clocks) -> 688: throw({error, [?MODULE, ?LINE, Weird, Clocks]}) 689: end, 690: {Proc, 0, 0, 0}). 691: 692: verify_callers(_, _, _, []) -> 693: ok; 694: verify_callers(Tab, Proc, MFA, [{Caller, Cnt, Acc, Own} | Tail]) -> 695: Id = {Proc, Caller}, 696: case ets:lookup(Tab, Id) of 697: [{Id, {_, {Caller, _, _, _}, Called}}] -> 698: case lists:keysearch(MFA, 1, Called) of 699: {value, {MFA, Cnt, Acc, Own}} -> 700: verify_callers(Tab, Proc, MFA, Tail); 701: false -> 702: throw({error, [?MODULE, ?LINE, MFA, Id]}) 703: end; 704: Weird -> 705: throw({error, [?MODULE, ?LINE, Weird]}) 706: end. 707: 708: verify_called(_, _, _, []) -> 709: ok; 710: verify_called(Tab, Proc, MFA, [{Called, Cnt, Acc, Own} | Tail]) -> 711: Id = {Proc, Called}, 712: case ets:lookup(Tab, Id) of 713: [{Id, {Callers, {Called, _, _, _}, _}}] -> 714: case lists:keysearch(MFA, 1, Callers) of 715: {value, {MFA, Cnt, Acc, Own}} -> 716: verify_called(Tab, Proc, MFA, Tail); 717: false -> 718: throw({error, [?MODULE, ?LINE, MFA, Id]}) 719: end; 720: Weird -> 721: throw({error, [?MODULE, ?LINE, Weird]}) 722: end. 723: 724: 725: 726: %% Parse a analysis file and return an Ets table with all function entries, 727: %% and a list of process entries. Checks the concistency of the function 728: %% entries when they are read. 729: parse(Filename) -> 730: case file:open(Filename, [read]) of 731: {ok, FD} -> 732: Result = parse_stream(FD), 733: file:close(FD), 734: Result; 735: Error -> 736: Error 737: end. 738: 739: parse_stream(FD) -> 740: Tab = ets:new(fprof_SUITE, []), 741: parse_stream(FD, Tab, [], void). 742: 743: parse_stream(FD, Tab, R, Proc) -> 744: case catch io:read(FD, '') of 745: {'EXIT', _} -> 746: {error, [?MODULE, ?LINE]}; 747: {ok, Term} -> 748: case parse_term(Term) of 749: {ok, {analysis_options, _} = Term_1} 750: when Proc == void -> 751: parse_stream(FD, Tab, [Term_1 | R], analysis_options); 752: {ok, [{totals, _, _, _} | _] = Term_1} 753: when Proc == analysis_options -> 754: parse_stream(FD, Tab, [Term_1 | R], totals); 755: {ok, [{P, _, _, _} | _] = Term_1} -> 756: parse_stream(FD, Tab, [Term_1 | R], P); 757: {ok, {_Callers, {MFA, _, _, _}, _Called} = Term_1} 758: when Proc == totals; is_list(Proc) -> 759: ets:insert(Tab, {{Proc, MFA}, Term_1}), 760: parse_stream(FD, Tab, R, Proc); 761: {ok, Term_1} -> 762: {error, [?MODULE, ?LINE, Term_1]}; 763: E -> 764: E 765: end; 766: eof -> 767: {ok, [Tab, lists:reverse(R)]}; 768: Error -> 769: Error 770: end. 771: 772: parse_term({Callers, Func, Called}) 773: when is_list(Callers), is_list(Called) -> 774: Callers_1 = lists:map(fun parse_clocks/1, Callers), 775: Func_1 = parse_clocks(Func), 776: Called_1 = lists:map(fun parse_clocks/1, Called), 777: Result = {Callers_1, Func_1, Called_1}, 778: case chk_invariant(Result) of 779: ok -> 780: {ok, Result}; 781: Error -> 782: Error 783: end; 784: parse_term([{_, _, _, _} = Clocks | Tail]) -> 785: {ok, [parse_clocks(Clocks) | Tail]}; 786: parse_term(Term) -> 787: {ok, Term}. 788: 789: parse_clocks({MFA, Cnt, undefined, Own}) -> 790: {MFA, Cnt, undefined, round(Own*1000)}; 791: parse_clocks({MFA, Cnt, Acc, Own}) -> 792: {MFA, Cnt, round(Acc*1000), round(Own*1000)}; 793: parse_clocks(Clocks) -> 794: Clocks. 795: 796: 797: 798: chk_invariant({Callers, {MFA, Cnt, Acc, Own}, Called} = Term) -> 799: {_, Callers_Cnt, Callers_Acc, Callers_Own} = Callers_Sum = sum(Callers), 800: % {_, Called_Cnt, Called_Acc, Called_Own} = Called_Sum = sum(Called), 801: case {MFA, 802: lists:keymember(suspend, 1, Callers), 803: lists:keymember(garbage_collect, 1, Callers), 804: Called} of 805: {suspend, false, _, []} -> 806: ok; 807: {suspend, _, _, _} = Weird -> 808: {error, [?MODULE, ?LINE, Weird, Term]}; 809: {garbage_collect, false, false, []} -> 810: ok; 811: {garbage_collect, false, false, [{suspend, _, _, _}]} -> 812: ok; 813: {garbage_collect, _, _, _} = Weird -> 814: {error, [?MODULE, ?LINE, Weird, Term]}; 815: {undefined, false, false, _} 816: when Callers == [], Cnt == 0, Acc == 0, Own == 0 -> 817: ok; 818: {undefined, _, _, _} = Weird -> 819: {error, [?MODULE, ?LINE, Weird, Term]}; 820: {_, _, _, _} -> 821: case chk_self_call(Term) of 822: true when Callers_Cnt /= Cnt; Callers_Acc /= Acc; 823: Callers_Own /= Own -> 824: {error, [?MODULE, ?LINE, Callers_Sum, Term]}; 825: % true when Called_Acc + Own /= Acc -> 826: % io:format("WARNING: ~p:~p, ~p, ~p.~n", 827: % [?MODULE, ?LINE, Term, Called_Sum]), 828: % {error, [?MODULE, ?LINE, Term, Called_Sum]}; 829: % ok; 830: true -> 831: ok; 832: false -> 833: {error, [?MODULE, ?LINE, Term]} 834: end 835: end. 836: 837: ts_sub({A, B, C}, {A0, B0, C0}) -> 838: ((A - A0)*1000000000000 + (B - B0))*1000000 + C - C0. 839: 840: sum(Funcs) -> 841: {sum, _Cnt, _Acc, _Own} = 842: lists:foldl( 843: fun ({_, C1, A1, O1}, {sum, C2, A2, O2}) -> 844: {sum, C1+C2, A1+A2, O1+O2} 845: end, 846: {sum, 0, 0, 0}, 847: Funcs). 848: 849: chk_self_call({Callers, {MFA, _Cnt, _Acc, _Own}, Called}) -> 850: case lists:keysearch(MFA, 1, Callers) of 851: false -> 852: true; 853: {value, {MFA, C, 0, O}} -> 854: case lists:keysearch(MFA, 1, Called) of 855: false -> 856: false; 857: {value, {MFA, C, 0, O}} -> 858: true; 859: {value, _} -> 860: false 861: end; 862: {value, _} -> 863: false 864: end. 865: 866: 867: 868: %%%--------------------------------------------------------------------- 869: %%% Fairly generic support functions 870: %%%--------------------------------------------------------------------- 871: 872: 873: ets_select_fold(Table, MatchSpec, Limit, Fun, Acc) -> 874: ets:safe_fixtable(Table, true), 875: ets_select_fold_1(ets:select(Table, MatchSpec, Limit), Fun, Acc). 876: 877: ets_select_fold_1('$end_of_table', _, Acc) -> 878: Acc; 879: ets_select_fold_1({Matches, Continuation}, Fun, Acc) -> 880: ets_select_fold_1(ets:select(Continuation), 881: Fun, 882: lists:foldl(Fun, Acc, Matches)). 883: 884: 885: 886: % ets_select_foreach(Table, MatchSpec, Limit, Fun) -> 887: % ets:safe_fixtable(Table, true), 888: % ets_select_foreach_1(ets:select(Table, MatchSpec, Limit), Fun). 889: 890: % ets_select_foreach_1('$end_of_table', _) -> 891: % ok; 892: % ets_select_foreach_1({Matches, Continuation}, Fun) -> 893: % lists:foreach(Fun, Matches), 894: % ets_select_foreach_1(ets:select(Continuation), Fun). 895: 896: 897: %%%--------------------------------------------------------------------- 898: %%% Simple smulation of fprof used for checking own and acc times for 899: %%% each function. 900: %%% The function 'undefined' is ignored 901: %%%--------------------------------------------------------------------- 902: 903: %% check_own_and_acc_traced(TraceFile, AnalysisFile) -> 904: %% check_own_and_acc(TraceFile, AnalysisFile, fun handle_trace_traced/2). 905: 906: check_own_and_acc(TraceFile, AnalysisFile) -> 907: check_own_and_acc(TraceFile, AnalysisFile, fun handle_trace/2). 908: 909: check_own_and_acc(TraceFile, AnalysisFile, HandlerFun) -> 910: dbg:trace_client(file,TraceFile,{HandlerFun,{init,self()}}), 911: receive {result,Result} -> 912: compare(Result,get_own_and_acc_from_analysis(AnalysisFile)) 913: end. 914: 915: %% handle_trace_traced(Trace, Msg) -> 916: %% io:format("handle_trace_traced(~p, ~p).", [Trace, Msg]), 917: %% handle_trace(Trace, Msg). 918: 919: handle_trace(Trace,{init,Parent}) -> 920: ?dbg("~p",[start]), 921: ets:new(fprof_verify_tab,[named_table]), 922: handle_trace(Trace,Parent); 923: handle_trace({trace_ts,Pid,in,MFA,TS},P) -> 924: ?dbg("~p",[{{in,Pid,MFA},get(Pid)}]), 925: case get(Pid) of 926: [suspend|[suspend|_]=NewStack] -> 927: T = ts_sub(TS,get({Pid,last_ts})), 928: update_acc(Pid,NewStack,T), 929: put(Pid,NewStack); 930: [suspend|NewStack] = Stack -> 931: T = ts_sub(TS,get({Pid,last_ts})), 932: update_acc(Pid,Stack,T), 933: put(Pid,NewStack); 934: [] -> 935: put(Pid,[MFA]), 936: insert(Pid,MFA); 937: undefined -> 938: put(first_ts,TS), 939: put(Pid,[MFA]), 940: insert(Pid,MFA) 941: end, 942: put({Pid,last_ts},TS), 943: P; 944: handle_trace({trace_ts,Pid,out,_MfaOrZero,TS},P) -> 945: ?dbg("~p",[{{out,Pid,_MfaOrZero},get(Pid)}]), 946: T = ts_sub(TS,get({Pid,last_ts})), 947: case get(Pid) of 948: [suspend|S] = Stack -> 949: update_acc(Pid,S,T), 950: put(Pid,[suspend|Stack]); 951: [MFA|_] = Stack -> 952: insert(Pid,suspend), 953: update_own(Pid,MFA,T), 954: update_acc(Pid,Stack,T), 955: put(Pid,[suspend|Stack]); 956: [] -> 957: insert(Pid,suspend), 958: put(Pid,[suspend]) 959: end, 960: put({Pid,last_ts},TS), 961: P; 962: handle_trace({trace_ts,Pid,call,MFA,{cp,Caller},TS},P) -> 963: ?dbg("~p",[{{call,Pid,MFA},get(Pid)}]), 964: T = ts_sub(TS,get({Pid,last_ts})), 965: case get(Pid) of 966: [MFA|_] = Stack -> 967: %% recursive 968: update_own(Pid,MFA,T), 969: update_acc(Pid,Stack,T); 970: [CallingMFA|_] = Stack when Caller==undefined -> 971: insert(Pid,MFA), 972: update_own(Pid,CallingMFA,T), 973: update_acc(Pid,Stack,T), 974: put(Pid,[MFA|Stack]); 975: [] when Caller==undefined -> 976: insert(Pid,MFA), 977: insert(Pid,MFA), 978: put(Pid,[MFA]); 979: Stack0 -> 980: Stack = [CallingMFA|_] = insert_caller(Caller,Stack0,[]), 981: insert(Pid,MFA), 982: insert(Pid,Caller), 983: update_own(Pid,CallingMFA,T), 984: update_acc(Pid,Stack,T), 985: put(Pid,[MFA|Stack]) 986: end, 987: put({Pid,last_ts},TS), 988: P; 989: handle_trace({trace_ts,Pid,return_to,MFA,TS},P) -> 990: ?dbg("~p",[{{return_to,Pid,MFA},get(Pid)}]), 991: T = ts_sub(TS,get({Pid,last_ts})), 992: case get(Pid) of 993: [MFA|_] = Stack -> 994: %% recursive 995: update_own(Pid,MFA,T), 996: update_acc(Pid,Stack,T), 997: put(Pid,Stack); 998: [ReturnFromMFA,MFA|RestOfStack] = Stack -> 999: update_own(Pid,ReturnFromMFA,T), 1000: update_acc(Pid,Stack,T), 1001: put(Pid,[MFA|RestOfStack]); 1002: [ReturnFromMFA|RestOfStack] = Stack -> 1003: update_own(Pid,ReturnFromMFA,T), 1004: update_acc(Pid,Stack,T), 1005: case find_return_to(MFA,RestOfStack) of 1006: [] when MFA==undefined -> 1007: put(Pid,[]); 1008: [] -> 1009: insert(Pid,MFA), 1010: put(Pid,[MFA]); 1011: NewStack -> 1012: put(Pid,NewStack) 1013: end 1014: end, 1015: put({Pid,last_ts},TS), 1016: P; 1017: handle_trace({trace_ts,Pid,gc_start,_,TS},P) -> 1018: ?dbg("~p",[{{gc_start,Pid},get(Pid)}]), 1019: case get(Pid) of 1020: [suspend|_] = Stack -> 1021: T = ts_sub(TS,get({Pid,last_ts})), 1022: insert(Pid,garbage_collect), 1023: update_acc(Pid,Stack,T), 1024: put(Pid,[garbage_collect|Stack]); 1025: [CallingMFA|_] = Stack -> 1026: T = ts_sub(TS,get({Pid,last_ts})), 1027: insert(Pid,garbage_collect), 1028: update_own(Pid,CallingMFA,T), 1029: update_acc(Pid,Stack,T), 1030: put(Pid,[garbage_collect|Stack]); 1031: undefined -> 1032: put(first_ts,TS), 1033: put(Pid,[garbage_collect]), 1034: insert(Pid,garbage_collect) 1035: end, 1036: put({Pid,last_ts},TS), 1037: P; 1038: handle_trace({trace_ts,Pid,gc_end,_,TS},P) -> 1039: ?dbg("~p",[{{gc_end,Pid},get(Pid)}]), 1040: T = ts_sub(TS,get({Pid,last_ts})), 1041: case get(Pid) of 1042: [garbage_collect|RestOfStack] = Stack -> 1043: update_own(Pid,garbage_collect,T), 1044: update_acc(Pid,Stack,T), 1045: put(Pid,RestOfStack) 1046: end, 1047: put({Pid,last_ts},TS), 1048: P; 1049: handle_trace({trace_ts,Pid,spawn,NewPid,{M,F,Args},TS},P) -> 1050: MFA = {M,F,length(Args)}, 1051: ?dbg("~p",[{{spawn,Pid,NewPid,MFA},get(Pid)}]), 1052: T = ts_sub(TS,get({Pid,last_ts})), 1053: put({NewPid,last_ts},TS), 1054: put(NewPid,[suspend,MFA]), 1055: insert(NewPid,suspend), 1056: insert(NewPid,MFA), 1057: case get(Pid) of 1058: [SpawningMFA|_] = Stack -> 1059: update_own(Pid,SpawningMFA,T), 1060: update_acc(Pid,Stack,T) 1061: end, 1062: put({Pid,last_ts},TS), 1063: P; 1064: handle_trace({trace_ts,Pid,exit,_Reason,TS},P) -> 1065: ?dbg("~p",[{{exit,Pid,_Reason},get(Pid)}]), 1066: T = ts_sub(TS,get({Pid,last_ts})), 1067: case get(Pid) of 1068: [DyingMFA|_] = Stack -> 1069: update_own(Pid,DyingMFA,T), 1070: update_acc(Pid,Stack,T), 1071: put(Pid,[]); 1072: [] -> 1073: ok 1074: end, 1075: put({Pid,last_ts},TS), 1076: P; 1077: handle_trace({trace_ts,_,Link,_,_},P) 1078: when Link==link; 1079: Link==unlink; 1080: Link==getting_linked; 1081: Link==getting_unlinked -> 1082: P; 1083: handle_trace(end_of_trace,P) -> 1084: ?dbg("~p",['end']), 1085: Result = ets:tab2list(fprof_verify_tab), 1086: {TotOwn,ProcOwns} = get_proc_owns(Result,[],0), 1087: TotAcc = ts_sub(get_last_ts(),get(first_ts)), 1088: P ! {result,[{totals,TotAcc,TotOwn}|ProcOwns]++Result}, 1089: P; 1090: handle_trace(Other,_P) -> 1091: exit({unexpected,Other}). 1092: 1093: find_return_to(MFA,[MFA|_]=Stack) -> 1094: Stack; 1095: find_return_to(MFA,[_|Stack]) -> 1096: find_return_to(MFA,Stack); 1097: find_return_to(_MFA,[]) -> 1098: []. 1099: 1100: insert_caller(MFA,[MFA|Rest],Result) -> 1101: lists:reverse(Result)++[MFA|Rest]; 1102: insert_caller(MFA,[Other|Rest],Result) -> 1103: insert_caller(MFA,Rest,[Other|Result]); 1104: insert_caller(MFA,[],Result) -> 1105: lists:reverse([MFA|Result]). 1106: 1107: insert(Pid,MFA) -> 1108: case ets:member(fprof_verify_tab,{Pid,MFA}) of 1109: false -> 1110: ets:insert(fprof_verify_tab,{{Pid,MFA},0,0}); 1111: true -> 1112: ok 1113: end. 1114: 1115: update_own(Pid,MFA,T) -> 1116: ets:update_counter(fprof_verify_tab,{Pid,MFA},{3,T}). 1117: 1118: update_acc(Pid,[MFA|Rest],T) -> 1119: case lists:member(MFA,Rest) of 1120: true -> 1121: %% Only charge one time for recursive functions 1122: ok; 1123: false -> 1124: ets:update_counter(fprof_verify_tab,{Pid,MFA},{2,T}) 1125: end, 1126: update_acc(Pid,Rest,T); 1127: update_acc(_Pid,[],_T) -> 1128: ok. 1129: 1130: 1131: get_last_ts() -> 1132: get_last_ts(get(),{0,0,0}). 1133: get_last_ts([{{_,last_ts},TS}|Rest],Last) when TS>Last -> 1134: get_last_ts(Rest,TS); 1135: get_last_ts([_|Rest],Last) -> 1136: get_last_ts(Rest,Last); 1137: get_last_ts([],Last) -> 1138: Last. 1139: 1140: get_proc_owns([{{Pid,_MFA},_Acc,Own}|Rest],Result,Sum) -> 1141: NewResult = 1142: case lists:keysearch(Pid,1,Result) of 1143: {value,{Pid,undefined,PidOwn}} -> 1144: lists:keyreplace(Pid,1,Result,{Pid,undefined,PidOwn+Own}); 1145: false -> 1146: [{Pid,undefined,Own}|Result] 1147: end, 1148: get_proc_owns(Rest,NewResult,Sum+Own); 1149: get_proc_owns([],Result,Sum) -> 1150: {Sum,Result}. 1151: 1152: 1153: compare([X|Rest],FprofResult) -> 1154: FprofResult1 = 1155: case lists:member(X,FprofResult) of 1156: true -> 1157: ?dbg("~p",[X]), 1158: lists:delete(X,FprofResult); 1159: false -> 1160: case lists:keysearch(element(1,X),1,FprofResult) of 1161: {value,Fprof} -> 1162: put(compare_error,true), 1163: io:format("Error: Different values\n" 1164: "Fprof: ~p\n" 1165: "Simulator: ~p",[Fprof,X]), 1166: lists:delete(Fprof,FprofResult); 1167: false -> 1168: put(compare_error,true), 1169: io:format("Error: Missing in fprof: ~p",[X]), 1170: FprofResult 1171: end 1172: end, 1173: compare(Rest,FprofResult1); 1174: compare([],Rest) -> 1175: case {remove_undefined(Rest,[]),get(compare_error)} of 1176: {[],undefined} -> ok; 1177: {Error,_} -> 1178: case Error of 1179: [] -> ok; 1180: _ -> io:format("\nMissing in simulator results:\n~p\n",[Error]) 1181: end, 1182: ?t:fail({error,mismatch_between_simulator_and_fprof}) 1183: end. 1184: 1185: remove_undefined([{{_Pid,undefined},_,_}|Rest],Result) -> 1186: remove_undefined(Rest,Result); 1187: remove_undefined([X|Rest],Result) -> 1188: remove_undefined(Rest,[X|Result]); 1189: remove_undefined([],Result) -> 1190: Result. 1191: 1192: get_own_and_acc_from_analysis(Log) -> 1193: case file:consult(Log) of 1194: {ok,[_Options,[{totals,_,TotAcc,TotOwn}]|Rest]} -> 1195: get_own_and_acc(undefined,Rest, 1196: [{totals,m1000(TotAcc),m1000(TotOwn)}]); 1197: Error -> 1198: exit({error,{cant_open,Log,Error}}) 1199: end. 1200: 1201: get_own_and_acc(_,[[{PidStr,_,Acc,Own}|_]|Rest],Result) -> 1202: Pid = list_to_pid(PidStr), 1203: get_own_and_acc(Pid,Rest,[{Pid,m1000(Acc),m1000(Own)}|Result]); 1204: get_own_and_acc(Pid,[{_Callers,{MFA,_,Acc,Own},_Called}|Rest],Result) -> 1205: get_own_and_acc(Pid,Rest,[{{Pid,MFA},m1000(Acc),m1000(Own)}|Result]); 1206: get_own_and_acc(_,[],Result) -> 1207: lists:reverse(Result). 1208: 1209: m1000(undefined) -> 1210: undefined; 1211: m1000(X) -> 1212: round(X*1000). 1213: