Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 5edfab1
Showing
15 changed files
with
969 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
*~ | ||
ebin | ||
.eunit | ||
erl_crash.dump | ||
deps |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# gcprof - garbage collection profiler | ||
|
||
gcprof is a tool to measure garbage collection in a production | ||
system. For every message a process receives, gcprof tells you how | ||
much time was spent in garbage collection and how many passes was | ||
done. | ||
|
||
|
||
## How does it work | ||
|
||
`gcprof:trace(pid(), identity_f())` triggers tracing of garbage | ||
collection in the specified process. Every time the process receives a | ||
message, the resulting garbage collections is associated with that | ||
message. The identity fun is used to identify that message. | ||
|
||
The runtime of garbage collections is aggregated and grouped on the | ||
identity. The results can be retrieved by calling | ||
`gcprof_aggregator:get_stats/0`. This will reset the current stats, so | ||
it is suitable for periodic polling. | ||
|
||
The convenience method `gcprof:trace_me(identity_f())` can be used | ||
from within the process you wish to trace. | ||
|
||
|
||
## Identity | ||
|
||
The identity fun is given the message your process received and must | ||
produce an identity. The results will be grouped by identity, so it | ||
makes sense to keep the number of unique ids low. The identity will be | ||
the `key` in `gcprof_aggregator:get_stats/0`. | ||
|
||
For example: | ||
|
||
fun ({'$gen_call', {_, _}, do_some_stuff}) -> | ||
my_identity; | ||
({'$gen_call', {_, _}, {request, Arg1, Arg2}) -> | ||
{request, Arg1}; | ||
(init) -> | ||
init; | ||
(_) -> | ||
undefined | ||
end | ||
|
||
There is one special message you may also handle and that is | ||
`init`. Any garbage collection done after triggering the trace, but | ||
before your process received any messages is associated with this | ||
identity. Please note that as starting a trace is asynchronous, some | ||
information might be lost. | ||
|
||
|
||
## Sampling | ||
|
||
As you may not want to trace all processes executing the code that | ||
triggers tracing, there is a very simple way of deciding which | ||
processes to sample. | ||
|
||
Example: | ||
|
||
IdentityF = fun ..., | ||
Id = 1000000, | ||
Divisor = 100, | ||
gcprof:sample_me(IdentityF, Id rem Divisor) | ||
|
||
`sample_me/2` only samples if the second argument is 0. So in the | ||
above case, given an even distribution of ids, only 1% will of the | ||
processes be sampled. | ||
|
||
|
||
## Impact on your system | ||
|
||
`gcprof:trace/2` uses a `gen_server:cast/2` under the hood so your | ||
process do not have to wait for gcprof. This also means that if gcprof | ||
is not running, nothing happens. | ||
|
||
If gcprof would crash, the processes would be restarted. When the | ||
gcprof tracer process goes away, the VM stops whatever traces was | ||
active. | ||
|
||
|
||
## Ideas for the future | ||
|
||
At the moment, there are some race conditions in some of the tests | ||
that makes them occasionally fail. This should be fixed. | ||
|
||
To trace code without modifying the target, a future version of gcprof | ||
could trace a specific function call and start tracing of the process | ||
invoking that function. Superuseful for the `init/1` gen_server | ||
callback for example. | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#!/bin/sh | ||
SCRIPT_PATH=`dirname $0` | ||
cd $SCRIPT_PATH/.. | ||
|
||
erl -pa deps/*/ebin ebin \ | ||
-boot start_sasl \ | ||
-s gcprof \ | ||
-K true \ | ||
+P 1000000 \ | ||
+A 8 \ | ||
-name gcprof@$(hostname) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{deps, [ | ||
{basho_stats, "1.0.1", {git, "git://github.com/basho/basho_stats.git", "master"}} | ||
]}. | ||
{cover_enabled, true}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
-module(example_server). | ||
|
||
-behaviour(gen_server). | ||
|
||
%% API | ||
-export([run/2]). | ||
|
||
%% gen_server callbacks | ||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, | ||
terminate/2, code_change/3]). | ||
|
||
-define(SERVER, ?MODULE). | ||
|
||
-record(state, {}). | ||
|
||
%%%=================================================================== | ||
%%% API | ||
%%%=================================================================== | ||
|
||
start(Id) -> | ||
gen_server:start(?MODULE, [Id], []). | ||
|
||
req(Pid) -> | ||
gen_server:call(Pid, create_garbage). | ||
|
||
run(Servers, Requests) -> | ||
RunF = fun(Id) -> | ||
{ok, Pid} = start(Id), | ||
timer:sleep(1), | ||
[req(Pid) || _ <- lists:seq(1, Requests)], | ||
exit(Pid, kill) | ||
end, | ||
|
||
lists:foreach(fun (Id) -> | ||
spawn(fun () -> RunF(Id) end) | ||
end, lists:seq(1, Servers)). | ||
|
||
|
||
%%%=================================================================== | ||
%%% gen_server callbacks | ||
%%%=================================================================== | ||
|
||
init([Id]) -> | ||
gcprof:sample_me(fun identity/1, Id rem 10), | ||
{ok, #state{}}. | ||
|
||
handle_call(create_garbage, _From, State) -> | ||
Pid = spawn(fun() -> | ||
receive _ -> ok | ||
end | ||
end), | ||
L = lists:seq(1, 100000), | ||
Pid ! L, | ||
{reply, ok, State}; | ||
|
||
handle_call(_Request, _From, State) -> | ||
Reply = ok, | ||
{reply, Reply, State}. | ||
|
||
handle_cast(_Msg, State) -> | ||
{noreply, State}. | ||
|
||
handle_info(_Info, State) -> | ||
{noreply, State}. | ||
|
||
terminate(_Reason, _State) -> | ||
ok. | ||
|
||
code_change(_OldVsn, State, _Extra) -> | ||
{ok, State}. | ||
|
||
%%%=================================================================== | ||
%%% Internal functions | ||
%%%=================================================================== | ||
|
||
identity({'$gen_call', {_, _}, create_garbage}) -> create_garbage; | ||
identity(_) -> undefined. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{application, gcprof, | ||
[ | ||
{description, ""}, | ||
{vsn, "1"}, | ||
{registered, []}, | ||
{applications, [ | ||
kernel, | ||
stdlib | ||
]}, | ||
{mod, { gcprof_app, []}}, | ||
{env, []} | ||
]}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
-module(gcprof). | ||
-export([start/0, stop/0]). | ||
-export([trace/2, trace_me/1, sample_me/2]). | ||
|
||
start() -> application:start(gcprof). | ||
stop() -> application:stop(gcprof). | ||
|
||
%% @doc: Starts tracing garbage collections in the given | ||
%% process. Note: gen_server:cast is used, so at some point in the | ||
%% future, tracing *might* start. | ||
trace(Pid, IdentityF) -> | ||
gen_server:cast(gcprof_server, {start_trace, Pid, IdentityF}). | ||
|
||
%% @doc: Starts tracing garbage collections in the calling process. | ||
trace_me(IdentityF) -> | ||
trace(self(), IdentityF). | ||
|
||
%% @doc: Traces the calling process if the second argument is | ||
%% 0. Allows tracing if the result of a modulo operation is 0. | ||
sample_me(IdentityF, 0) -> | ||
trace(self(), IdentityF); | ||
sample_me(_, _) -> | ||
ok. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
-module(gcprof_aggregator). | ||
|
||
-behaviour(gen_server). | ||
|
||
%% API | ||
-export([start_link/0, results/4, get_stats/0]). | ||
|
||
%% gen_server callbacks | ||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, | ||
terminate/2, code_change/3]). | ||
|
||
-define(SERVER, ?MODULE). | ||
-define(NEW_HISTOGRAM, basho_stats_histogram:new(0, 1000000, 1000000)). | ||
|
||
-record(state, {}). | ||
|
||
%%%=================================================================== | ||
%%% API | ||
%%%=================================================================== | ||
|
||
start_link() -> | ||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). | ||
|
||
results(Pid, Identity, Runtime, Invocations) -> | ||
gen_server:cast(?MODULE, {results, Pid, Identity, Runtime, Invocations}). | ||
|
||
get_stats() -> | ||
gen_server:call(?MODULE, get_stats). | ||
|
||
%%%=================================================================== | ||
%%% gen_server callbacks | ||
%%%=================================================================== | ||
|
||
init([]) -> | ||
{ok, #state{}}. | ||
|
||
handle_call(get_stats, _From, State) -> | ||
{reply, {ok, do_get_stats()}, State}; | ||
|
||
handle_call(_Request, _From, State) -> | ||
Reply = ok, | ||
{reply, Reply, State}. | ||
|
||
handle_cast({results, _Pid, Identity, Runtime, Invocations}, State) -> | ||
ok = update_histogram({Identity, runtime}, Runtime), | ||
ok = update_histogram({Identity, invocations}, Invocations), | ||
%%ok = update_histogram({Pid, runtime}, Runtime), | ||
%%ok = update_histogram({Pid, invocations}, Invocations), | ||
{noreply, State}; | ||
|
||
handle_cast(_Msg, State) -> | ||
{noreply, State}. | ||
|
||
|
||
handle_info(_Info, State) -> | ||
{noreply, State}. | ||
|
||
terminate(_Reason, _State) -> | ||
ok. | ||
|
||
code_change(_OldVsn, State, _Extra) -> | ||
{ok, State}. | ||
|
||
%%%=================================================================== | ||
%%% Internal functions | ||
%%%=================================================================== | ||
|
||
get_or_create_histogram(Key) -> | ||
case get(Key) of | ||
undefined -> ?NEW_HISTOGRAM; | ||
{histogram, Value} -> Value | ||
end. | ||
|
||
update_histogram(Key, Value) -> | ||
Hist = get_or_create_histogram(Key), | ||
NewHist = basho_stats_histogram:update(Value, Hist), | ||
erlang:put(Key, {histogram, NewHist}), | ||
ok. | ||
|
||
|
||
do_get_stats() -> | ||
Ts = lists:filter(fun ({_, {histogram, Hist}}) -> | ||
basho_stats_histogram:observations(Hist) =/= 0; | ||
(_) -> | ||
false | ||
end, get()), | ||
[erlang:put(Key, {histogram, ?NEW_HISTOGRAM}) || {Key, _Value} <- Ts], | ||
lists:map(fun (T) -> format_timing(T) end, Ts). | ||
|
||
format_timing({Identity, {histogram, Hist}}) -> | ||
{Min, Mean, Max, _, SD} = | ||
basho_stats_histogram:summary_stats(Hist), | ||
|
||
[ | ||
{key, Identity}, | ||
{observations, basho_stats_histogram:observations(Hist)}, | ||
{min, Min}, | ||
{mean, Mean}, | ||
{max, Max}, | ||
{sd, SD}, | ||
{quantile_25, basho_stats_histogram:quantile(0.250, Hist)}, | ||
{quantile_75, basho_stats_histogram:quantile(0.750, Hist)}, | ||
{quantile_99, basho_stats_histogram:quantile(0.990, Hist)}, | ||
{quantile_999, basho_stats_histogram:quantile(0.999, Hist)} | ||
]. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
-module(gcprof_app). | ||
|
||
-behaviour(application). | ||
|
||
%% Application callbacks | ||
-export([start/2, stop/1]). | ||
|
||
%% =================================================================== | ||
%% Application callbacks | ||
%% =================================================================== | ||
|
||
start(_StartType, _StartArgs) -> | ||
gcprof_sup:start_link(). | ||
|
||
stop(_State) -> | ||
ok. |
Oops, something went wrong.