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). -module(code_path_no_recurse_rt).
-export([files/0, -export([files/0,
run/1]). run/1]).

View file

@ -1,5 +1,29 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et %% 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). -module(erlc_rt).
-export([files/0, -export([files/0,
run/1]). run/1]).

View file

@ -1,4 +1,4 @@
syntax error 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 compile files like OS X resource forks and should not be
processed at all 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). -module(logging_rt).
-export([files/0, -export([files/0,
run/1]). run/1]).
@ -72,7 +98,7 @@ check_output1(Cmd, Captured, Expected, Unexpected) ->
false; false;
{match, [Match]} -> {match, [Match]} ->
retest:log( retest:log(
console, error,
"Unexpected output when running cmd '~s':~n~s~n", "Unexpected output when running cmd '~s':~n~s~n",
[Cmd, Match]), [Cmd, Match]),
{true, 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= \ appid= \
overlay_vars= \ overlay_vars= \
previous_release= \ previous_release= \
profiler= \
nodeid= \ nodeid= \
root_dir= \ root_dir= \
skip_deps=true \ skip_deps=true \

View file

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

View file

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

View file

@ -104,13 +104,8 @@ run(RawArgs) ->
case rebar_config:get_xconf(BaseConfig1, enable_profiling, false) of case rebar_config:get_xconf(BaseConfig1, enable_profiling, false) of
true -> true ->
io:format("Profiling!\n"), ?CONSOLE("Profiling!\n", []),
try profile(BaseConfig1, Cmds);
fprof:apply(fun run_aux/2, [BaseConfig1, Cmds])
after
ok = fprof:profile(),
ok = fprof:analyse([{dest, "fprof.analysis"}])
end;
false -> false ->
run_aux(BaseConfig1, Cmds) run_aux(BaseConfig1, Cmds)
end. end.
@ -160,6 +155,42 @@ init_config1(BaseConfig) ->
AbsCwd = filename:absname(rebar_utils:get_cwd()), AbsCwd = filename:absname(rebar_utils:get_cwd()),
rebar_config:set_xconf(BaseConfig1, base_dir, AbsCwd). 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) -> run_aux(BaseConfig, Commands) ->
%% Make sure crypto is running %% Make sure crypto is running
case crypto:start() of case crypto:start() of
@ -439,7 +470,10 @@ option_spec_list() ->
{defines, $D, undefined, string, "Define compiler macro"}, {defines, $D, undefined, string, "Define compiler macro"},
{jobs, $j, "jobs", integer, JobsHelp}, {jobs, $j, "jobs", integer, JobsHelp},
{config, $C, "config", string, "Rebar config file to use"}, {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_going, $k, "keep-going", undefined,
"Keep running after a command fails"}, "Keep running after a command fails"},
{recursive, $r, "recursive", boolean, {recursive, $r, "recursive", boolean,