Merge pull request #337 from tuncer/eflame

Implement eflame -p/--profile support
This commit is contained in:
Fred Hebert 2014-10-30 11:54:20 -04:00
commit 5f7a5afa75
9 changed files with 305 additions and 12 deletions

View file

@ -1,3 +1,29 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%% -------------------------------------------------------------------
%%
%% rebar: Erlang Build Tools
%%
%% Copyright (c) 2014 Tuncer Ayaz
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
%% in the Software without restriction, including without limitation the rights
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the Software is
%% furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be included in
%% all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%% THE SOFTWARE.
%% -------------------------------------------------------------------
-module(code_path_no_recurse_rt).
-export([files/0,
run/1]).

View file

@ -1,5 +1,29 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%% -------------------------------------------------------------------
%%
%% rebar: Erlang Build Tools
%%
%% Copyright (c) 2014 Tuncer Ayaz
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
%% in the Software without restriction, including without limitation the rights
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the Software is
%% furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be included in
%% all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%% THE SOFTWARE.
%% -------------------------------------------------------------------
-module(erlc_rt).
-export([files/0,
run/1]).

View file

@ -1,4 +1,4 @@
syntax error
this is file is here to verify that rebar does not try to
this file is here to verify that rebar does not try to
compile files like OS X resource forks and should not be
processed at all

View file

@ -1,3 +1,29 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%% -------------------------------------------------------------------
%%
%% rebar: Erlang Build Tools
%%
%% Copyright (c) 2014 Tuncer Ayaz
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
%% in the Software without restriction, including without limitation the rights
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the Software is
%% furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be included in
%% all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%% THE SOFTWARE.
%% -------------------------------------------------------------------
-module(logging_rt).
-export([files/0,
run/1]).
@ -72,7 +98,7 @@ check_output1(Cmd, Captured, Expected, Unexpected) ->
false;
{match, [Match]} ->
retest:log(
console,
error,
"Unexpected output when running cmd '~s':~n~s~n",
[Cmd, Match]),
{true, Match}

View file

@ -0,0 +1,180 @@
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
%% -------------------------------------------------------------------
%%
%% rebar: Erlang Build Tools
%%
%% Copyright (c) 2014 Tuncer Ayaz
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
%% in the Software without restriction, including without limitation the rights
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the Software is
%% furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be included in
%% all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%% THE SOFTWARE.
%% -------------------------------------------------------------------
-module(profile_rt).
-export([files/0,
run/1]).
-include_lib("eunit/include/eunit.hrl").
files() ->
[
{copy, "../../rebar", "rebar"}
].
run(_Dir) ->
Cmd = "./rebar list-deps",
FprofFiles = ["fprof.trace", "fprof.analysis"],
EflameFiles = ["eflame.trace", "eflame.svg"],
%% run a simple command (list-deps) without profiling
SharedExpected = "==> profile_rt \\(list-deps\\)",
%% run Cmd without profiling
retest:log(info, "Check '~s' without profiling~n", [Cmd]),
ok = check(Cmd, should_succeed, [SharedExpected], ["Profiling!"],
[], FprofFiles ++ EflameFiles),
%% run Cmd with fprof profiling
retest:log(info, "Check '~s' with fprof profiling~n", [Cmd]),
ok = check(Cmd ++ " -p", should_succeed,
[SharedExpected, "Profiling!", "See fprof\.analysis"],
["See eflame\.svg"],
FprofFiles, EflameFiles),
delete_files(FprofFiles),
%% run Cmd with explicitly selected fprof profiling
retest:log(info, "Check '~s' with explicitly selected fprof profiling~n",
[Cmd]),
ok = check(Cmd ++ " -p profiler=fprof", should_succeed,
[SharedExpected, "Profiling!", "See fprof\.analysis"],
["See eflame\.svg"],
FprofFiles, EflameFiles),
delete_files(FprofFiles),
case code:lib_dir(eflame) of
{error, bad_name} ->
retest:log(info,
"eflame not found in code path. skip eflame test~n"),
ok;
_EflameDir ->
%% run Cmd with eflame profiling
retest:log(info, "Check '~s' with eflame profiling~n", [Cmd]),
ok = check(Cmd ++ " -p profiler=eflame", should_succeed,
[SharedExpected, "Profiling!", "See eflame\.svg"],
["See fprof\.analysis"],
EflameFiles, FprofFiles),
delete_files(EflameFiles)
end,
ok.
check(Cmd, FailureMode, ExpectedOutput, UnexpectedOutput,
ExpectedFiles, UnexpectedFiles) ->
case {retest:sh(Cmd), FailureMode} of
{{error, _}=Error, should_succeed} ->
retest:log(error, "cmd '~s' failed:~n~p~n", [Cmd, Error]),
Error;
{{ok, CapturedOutput}, should_succeed} ->
JoinedOutput = string:join(CapturedOutput, "\n"),
check1(Cmd, JoinedOutput, ExpectedOutput, UnexpectedOutput,
ExpectedFiles, UnexpectedFiles);
{{error, {stopped, {_Rc, CapturedOutput}}}, should_fail} ->
JoinedOutput = string:join(CapturedOutput, "\n"),
check1(Cmd, JoinedOutput, ExpectedOutput, UnexpectedOutput,
ExpectedFiles, UnexpectedFiles)
end.
check1(Cmd, CapturedOutput, ExpectedOutput, UnexpectedOutput,
ExpectedFiles, UnexpectedFiles) ->
ReOpts = [{capture, all, list}],
ExMatches =
lists:zf(
fun(Pattern) ->
case re:run(CapturedOutput, Pattern, ReOpts) of
nomatch ->
retest:log(error,
"Expected pattern '~s' missing "
"in the following output:~n"
"=== BEGIN ===~n~s~n=== END ===~n",
[Pattern, CapturedOutput]),
{true, Pattern};
{match, _} ->
false
end
end, ExpectedOutput),
UnExMatches =
lists:zf(
fun(Pattern) ->
case re:run(CapturedOutput, Pattern, ReOpts) of
nomatch ->
false;
{match, [Match]} ->
retest:log(
console,
"Unexpected output when running cmd '~s':~n~s~n",
[Cmd, Match]),
{true, Match}
end
end, UnexpectedOutput),
ExFiles =
lists:zf(
fun(File) ->
case filelib:is_regular(File) of
true ->
false;
false ->
retest:log(error,
"Expected file missing: ~s~n", [File]),
{true, File}
end
end, ExpectedFiles),
UnExFiles =
lists:zf(
fun(File) ->
case filelib:is_regular(File) of
true ->
retest:log(error,
"Unexpected file found: ~s~n", [File]),
{true, File};
false ->
false
end
end, UnexpectedFiles),
case {ExMatches, UnExMatches, ExFiles, UnExFiles} of
{[], [], [], []} ->
ok;
_ ->
error
end.
delete_files(Files) ->
lists:foreach(fun(File) -> ok = file:delete(File) end, Files).
%%
%% Generate the contents of a simple .app file
%%
app(Name, Modules) ->
App = {application, Name,
[{description, atom_to_list(Name)},
{vsn, "1"},
{modules, Modules},
{registered, []},
{applications, [kernel, stdlib]}]},
io_lib:format("~p.\n", [App]).

View file

@ -53,6 +53,7 @@ _rebar()
appid= \
overlay_vars= \
previous_release= \
profiler= \
nodeid= \
root_dir= \
skip_deps=true \

View file

@ -63,6 +63,7 @@ _rebar () {
'appid[Application id]:' \
'overlay_vars[Overlay variables file]:' \
'previous_release[Previous release path]:' \
'profiler[Select profiler]::flag:(fprof eflame)' \
'nodeid[Node id]:' \
'root_dir[Reltool config root directory]::directory:_files -/' \
'skip_deps[Skip deps]::flag:(true false)' \

View file

@ -19,6 +19,7 @@
[{"(XC - UC) || (XU - X - B
- (\"escript\":\"foldl\"/\"3\")
- (\"eunit_test\":\"function_wrapper\"/\"2\")
- (\"eflame\":\"apply\"/\"5\")
- (\"abnfc\":\"file\"/\"2\")
- (\"erlydtl\":\"compile\"/\"3\")
- (\"lfe_comp\":\"file\"/\"2\")

View file

@ -104,13 +104,8 @@ run(RawArgs) ->
case rebar_config:get_xconf(BaseConfig1, enable_profiling, false) of
true ->
io:format("Profiling!\n"),
try
fprof:apply(fun run_aux/2, [BaseConfig1, Cmds])
after
ok = fprof:profile(),
ok = fprof:analyse([{dest, "fprof.analysis"}])
end;
?CONSOLE("Profiling!\n", []),
profile(BaseConfig1, Cmds);
false ->
run_aux(BaseConfig1, Cmds)
end.
@ -160,6 +155,42 @@ init_config1(BaseConfig) ->
AbsCwd = filename:absname(rebar_utils:get_cwd()),
rebar_config:set_xconf(BaseConfig1, base_dir, AbsCwd).
profile(BaseConfig1, Commands) ->
Profiler = rebar_config:get_global(BaseConfig1, profiler, "fprof"),
profile(BaseConfig1, Commands, list_to_atom(Profiler)).
profile(Config, Commands, fprof) ->
try
fprof:apply(fun run_aux/2, [Config, Commands])
after
ok = fprof:profile(),
ok = fprof:analyse([{dest, "fprof.analysis"}]),
?CONSOLE("See fprof.analysis (generated from fprof.trace)~n", []),
ok
end;
profile(Config, Commands, eflame) ->
case code:lib_dir(eflame) of
{error, bad_name} ->
?ABORT("eflame not found in code path~n", []),
ok;
EflameDir ->
Trace = "eflame.trace",
try
eflame:apply(normal_with_children, Trace,
rebar, run, [Config, Commands])
after
%% generate flame graph
Script = filename:join(EflameDir, "stack_to_flame.sh"),
Svg = "eflame.svg",
%% stack_to_flame.sh < eflame.trace > eflame.png
Cmd = ?FMT("~s < ~s > ~s", [Script, Trace, Svg]),
{ok, []} = rebar_utils:sh(Cmd, [{use_stdout, false},
abort_on_error]),
?CONSOLE("See eflame.svg (generated from eflame.trace)~n", []),
ok
end
end.
run_aux(BaseConfig, Commands) ->
%% Make sure crypto is running
case crypto:start() of
@ -439,7 +470,10 @@ option_spec_list() ->
{defines, $D, undefined, string, "Define compiler macro"},
{jobs, $j, "jobs", integer, JobsHelp},
{config, $C, "config", string, "Rebar config file to use"},
{profile, $p, "profile", undefined, "Profile this run of rebar"},
{profile, $p, "profile", undefined,
"Profile this run of rebar. Via profiler= you can optionally select "
"either fprof (default) or eflame. The result can be found in "
"fprof.analysis or eflame.svg."},
{keep_going, $k, "keep-going", undefined,
"Keep running after a command fails"},
{recursive, $r, "recursive", boolean,