Skip to content

Instantly share code, notes, and snippets.

@essen
Last active July 11, 2017 19:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save essen/2a5739fe1027073c80cb0dc0435e872d to your computer and use it in GitHub Desktop.
Save essen/2a5739fe1027073c80cb0dc0435e872d to your computer and use it in GitHub Desktop.
diff --git a/c_src/lg_tracer.c b/c_src/lg_tracer.c
index 8551dbc..6110d7a 100644
--- a/c_src/lg_tracer.c
+++ b/c_src/lg_tracer.c
@@ -111,6 +111,9 @@ NIF_FUNCTION(enabled)
ERL_NIF_TERM tracers, value;
ErlNifPid tracer;
+ // @todo We can go one step further by having the one pid
+ // in its own value in the map, skipping a get_map_value step.
+
// This function will only be called for trace_status.
// We can take a few shortcuts knowing this.
@@ -138,6 +141,11 @@ NIF_FUNCTION(enabled)
NIF_FUNCTION(enabled_call)
{
+ // @todo Discard trace events for a percent of processes.
+
+// if ((enif_hash(ERL_NIF_INTERNAL_HASH, argv[2], 0) % 100) > 10)
+// return atom_discard;
+
// We always want both call and return_to.
return atom_trace;
}
@@ -153,17 +161,32 @@ NIF_FUNCTION(enabled_procs)
return atom_discard;
}
+ // @todo Discard trace events for a percent of processes.
+
+// if ((enif_hash(ERL_NIF_INTERNAL_HASH, argv[2], 0) % 100) > 10)
+// return atom_discard;
+
return atom_trace;
}
NIF_FUNCTION(enabled_running_procs)
{
+ // @todo Discard trace events for a percent of processes.
+
+// if ((enif_hash(ERL_NIF_INTERNAL_HASH, argv[2], 0) % 100) > 10)
+// return atom_discard;
+
// We always want both in and out.
return atom_trace;
}
NIF_FUNCTION(enabled_send)
{
+ // @todo Discard trace events for a percent of processes.
+
+// if ((enif_hash(ERL_NIF_INTERNAL_HASH, argv[2], 0) % 100) > 10)
+// return atom_discard;
+
// We always want both send and send_to_non_existing_process.
return atom_trace;
}
@@ -218,6 +241,8 @@ NIF_FUNCTION(trace)
//
// - {Tag, Tracee, Ts, Term}
// - {Tag, Tracee, Ts, Term, Extra}
+ //
+ // @todo The tag can probably be made an integer instead?
if (enif_get_map_value(env, argv[4], atom_extra, &extra))
msg = enif_make_tuple5(env, argv[0], argv[2], ts, argv[3], extra);
diff --git a/test/lg_SUITE.erl b/test/lg_SUITE.erl
index 99e559c..ca0a69e 100644
--- a/test/lg_SUITE.erl
+++ b/test/lg_SUITE.erl
@@ -76,8 +76,20 @@ running_true(Config) ->
send_true(Config) ->
doc("Trace a specific module with send option enabled."),
lg:trace(lists, lg_file_tracer, config(priv_dir, Config) ++ "/send_true.lz4",
- #{send => true}),
- lists:seq(1,10),
+ #{pool_size => 1, send => true}),
+ Self = self(),
+ %% Send a message to and from an existing process.
+ Pid = spawn(fun() ->
+ receive {msg_from, Self} ->
+ Self ! {msg_from, self()}
+ end
+ end),
+ Pid ! {msg_from, Self},
+ receive {msg_from, Pid} -> ok end,
+ %% Also send a message to a non existing process.
+ DeadPid = spawn(fun() -> ok end),
+ receive after 100 -> ok end,
+ DeadPid ! {msg_from, Self},
lg:stop(),
do_ensure_decompress(config(priv_dir, Config) ++ "/send_true.lz4").
%% Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved.
%%
%% This package, Looking Glass, is double-licensed under the Mozilla
%% Public License 1.1 ("MPL") and the Apache License version 2
%% ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL,
%% please see LICENSE-APACHE2.
%%
%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
%% either express or implied. See the LICENSE file for specific language governing
%% rights and limitations of this software.
%%
%% If you have any questions regarding licensing, please contact us at
%% info@rabbitmq.com.
-module(lg_messages).
-export([profile/1]).
-export([profile_many/1]).
-record(state, {
processes = #{} :: #{pid() => pos_integer()},
pairs = #{} :: #{{pid(), pid()} => pos_integer()},
non_existing = #{} :: #{pid() => pos_integer()},
calls = #{} :: #{pid() => atom()}
}).
-spec profile(file:filename_all()) -> ok.
profile(Input) ->
{ok, FinalState} = lg_file_reader:fold(fun handle_event/2, #state{}, Input),
flush(FinalState).
-spec profile_many(file:filename()) -> ok.
profile_many(Wildcard) ->
Files = filelib:wildcard(Wildcard),
FinalState = lists:foldl(fun(Input, State0) ->
{ok, State} = lg_file_reader:fold(fun handle_event/2, State0, Input),
State
end, #state{}, Files),
flush(FinalState).
%% @todo Later we may want to look at the latency of gen_server call/reply.
%% @todo Later we may want to look at particular messages, have some sort of callback.
handle_event({send, From, _, Msg, To}, State=#state{processes=Procs, pairs=Pairs, calls=Calls}) ->
ProcsCount = maps:get(From, Procs, 0),
PairsCount = maps:get({From, To}, Pairs, 0),
State#state{processes=Procs#{From => ProcsCount + 1},
pairs=Pairs#{{From, To} => PairsCount + 1},
calls=Calls#{From => Msg}};
handle_event({send_to_non_existing_process, From, _, _, _},
State=#state{non_existing=Map}) ->
Count = maps:get(From, Map, 0),
State#state{non_existing=Map#{From => Count + 1}};
%% We save the most recent call for each process for identification.
%%handle_event({call, Pid, _, MFA}, State=#state{calls=Calls}) ->
%% Acc = maps:get(Pid, Calls, []),
%% State#state{calls=Calls#{Pid => [MFA|Acc]}};
%% Ignore all other events. We only care about messages.
handle_event(_, State) ->
State.
%% Output of the profiling.
flush(State) ->
flush_most_active_processes(State),
flush_most_non_existing(State),
flush_most_active_pair_unidirectional(State),
flush_most_active_pair_bidirectional(State),
io:format("~n"),
ok.
flush_most_active_processes(State=#state{processes=Procs}) ->
L = lists:sublist(
lists:reverse(lists:keysort(2, maps:to_list(Procs))),
1, 100),
io:format("~nThey send the most messages:~n~n Pid Count Most recent call~n"),
_ = [io:format(" ~p ~p ~p~n", [P, C, mfa(P, State)]) || {P, C} <- L],
ok.
flush_most_non_existing(State=#state{non_existing=Procs}) ->
L = lists:sublist(
lists:reverse(lists:keysort(2, maps:to_list(Procs))),
1, 100),
io:format("~nThey send the most messages to dead processes:~n~n Pid Count Most recent call~n"),
_ = [io:format(" ~p ~p ~p~n", [P, C, mfa(P, State)]) || {P, C} <- L],
ok.
flush_most_active_pair_unidirectional(#state{pairs=Procs}) ->
L = lists:sublist(
lists:reverse(lists:keysort(2, maps:to_list(Procs))),
1, 100),
io:format("~nThey send the most messages to this other process:~n~n"
" From To Count~n"),
_ = [io:format(" ~p ~p ~p~n", [F, T, C]) || {{F, T}, C} <- L],
ok.
flush_most_active_pair_bidirectional(#state{pairs=Procs0}) ->
Procs = maps:fold(fun merge_pairs/3, #{}, Procs0),
L = lists:sublist(
lists:reverse(lists:keysort(2, maps:to_list(Procs))),
1, 100),
io:format("~nThey send the most messages to each other:~n~n"
" Pid 1 Pid 2 Count~n"),
_ = [io:format(" ~p ~p ~p~n", [F, T, C]) || {{F, T}, C} <- L],
ok.
mfa(Pid, #state{calls=Calls}) ->
%% @todo convert_mfa from lg_callgrind.
maps:get(Pid, Calls, 'No call recorded.').
merge_pairs({From, To}, Count, Acc) ->
Key = if
From < To -> {From, To};
true -> {To, From}
end,
AccCount = maps:get(Key, Acc, 0),
Acc#{Key => AccCount + Count}.
%% which process sends the most messages
%% which process sends the most messages to non existent processes
%% which p1->p2 sees the most activity
%% which p1->p2 + p2->p1 sees the most activity
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment