Skip to content

Instantly share code, notes, and snippets.

@wudeng
Forked from wardbekker/user_default.erl
Last active December 21, 2015 06:59
Show Gist options
  • Save wudeng/6267991 to your computer and use it in GitHub Desktop.
Save wudeng/6267991 to your computer and use it in GitHub Desktop.
-module(user_default).
-author('wudeng@4399.net').
-compile(export_all).
-import(io, [format/1]).
help() ->
shell_default:help(),
format("** user extended commands **~n"),
format("dbgtc(File) -- use dbg:trace_client() to read data from File\n"),
format("dbgon(M) -- enable dbg tracer on all funs in module M\n"),
format("dbgon(M,Fun) -- enable dbg tracer for module M and function F\n"),
format("dbgon(M,File) -- enable dbg tracer for module M and log to File\n"),
format("dbgadd(M) -- enable call tracer for module M\n"),
format("dbgadd(M,F) -- enable call tracer for function M:F\n"),
format("dbgdel(M) -- disable call tracer for module M\n"),
format("dbgdel(M,F) -- disable call tracer for function M:F\n"),
format("dbgoff() -- disable dbg tracer (calls dbg:stop/0)\n"),
format("r() -- compile and load all changed modules\n"),
format("l() -- load all changed modules\n"),
format("la() -- load all modules\n"),
% format("nl() -- load all changed modules on all known nodes\n"),
format("mm() -- list modified modules\n"),
true.
top() ->
spawn(fun()->etop:start([{output,text},{interval,3},{lines,20}, {sort, memory}])end).
%% spawn(fun()->etop:start([{output,text},{interval,3},{lines,20}, {sort, memory}])end).
%% spawn(fun()->etop:start([{output,text},{interval,3},{lines,20}, {sort, reductions}])end).
%% spawn(fun()->etop:start([{output,text},{interval,3},{lines,20}, {sort, msg_q}])end).
%% spawn(fun()->etop:start([{output,text},{interval,3},{lines,20}, {sort, runtime}])end).
%% etop:stop().
%%
binary_leak(N) ->
lists:sublist(
lists:usort(
fun({K1,V1},{K2,V2}) -> {V1,K1} =< {V2,K2} end,
[try
{_,Pre} = erlang:process_info(Pid, binary),
erlang:garbage_collect(Pid),
{_,Post} = erlang:process_info(Pid, binary),
{Pid, length(Post)-length(Pre)}
catch
_:_ -> {Pid, 0}
end || Pid <- processes()]),
N).
now_time() ->
{A, B, _} = erlang:now(),
A * 1000000 + B.
fprof(Procs) ->
fprof:trace([start, {file, "fprof.trace"}, {procs, Procs}]).
stop_fprof() ->
ok = fprof:trace(stop),
ok = fprof:profile({file, "fprof.trace"}),
Analyse = lists:concat(["fprof-", now_time(), ".analysis"]),
% {sort, own}
ok = fprof:analyse([{dest, Analyse}, {details, true}, {totals, true}, {sort, own}]),
io:format(("fprof result:~s\n"), [Analyse]),
ok.
eprof(Procs) ->
eprof:start_profiling(Procs).
stop_eprof() ->
eprof:stop_profiling(),
eprof:log("procs.profile"),
eprof:analyze(procs),
eprof:log("total.profile"),
eprof:analyze(total).
rb(Module) ->
rb(100000, 1000, Module).
rb(Time, Msg, Module) ->
redbug:start(Time, Msg, {Module, [return]}).
rba(Module) ->
rba(100000, 1000, Module).
%% redbug:start("mh_scene_sup:get_scene_pid(SceneId) when is_integer(SceneId) -> return;stack", [{time, 1000000000}, {msgs, 100000}]).
rba(Time, Msg, Module) ->
redbug:start(Time, Msg, {Module, [return,arity]}).
o() ->
spawn(fun()-> observer:start() end).
t(T) ->
ets:tab2list(T).
p(Arg) ->
io:format("~ts~n",[Arg]).
r() ->
make:all([netload]).
tracer(Total) ->
dbg:tracer(process, {fun(_,N) when N >= Total-> dbg:stop_clear(); (Msg, N) -> io:format("~p~n", [Msg]), N+1 end, 0}).
dbgtc(File) ->
Fun = fun({trace,_,call,{M,F,A}}, _) ->
io:format("call: ~w:~w~w~n", [M,F,A]);
({trace,_,return_from,{M,F,A},R}, _) ->
io:format("retn: ~w:~w/~w -> ~w~n", [M,F,A,R]);
(A,B) ->
io:format("~w: ~w~n", [A,B])
end,
dbg:trace_client(file, File, {Fun, []}).
%% trace远程节点
dbgn(Node, Module) ->
dbgn(Node, Module, 100).
dbgn(Node, Module, N) ->
case tracer(N) of
{ok, _} ->
{ok, _} = dbg:n(Node),
dbg:p(all, call),
dbg:tpl(Module, [{'_',[],[{return_trace}]}]),
ok;
Else ->
Else
end.
dbgon(Module) ->
dbgon(Module, 100).
dbgon(Module, N) when is_integer(N) ->
case tracer(N) of
{ok,_} ->
dbg:p(all,call),
dbg:tpl(Module, [{'_',[],[{return_trace}]}]),
ok;
Else ->
Else
end;
dbgon(Module, Fun) when is_atom(Fun) ->
dbgon(Module, Fun, 100);
dbgon(Module, File) when is_list(File) ->
{ok,_} = dbg:tracer(port, dbg:trace_port(file, File)),
dbg:p(all,call),
%%dbg:p(all,[call, running, garbage_collection, timestamp, return_to]),
%%dbg:tpl(Module, [{'_',[],[{return_trace}]}]),
dbg:tpl(Module, [{'_',[],[{return_trace},{exception_trace}]}]),
ok.
dbgon(Module, Fun, File) when is_list(File) ->
{ok,_} = dbg:tracer(port, dbg:trace_port(file, File)),
dbg:p(all,call),
dbg:tpl(Module, Fun, [{'_',[],[{return_trace},{exception_trace}]}]),
ok;
dbgon(Module, Fun, N) when is_atom(Fun) ->
{ok,_} = tracer(N),
dbg:p(all,call),
%%dbg:tpl(Module, Fun, [{'_',[],[{return_trace}]}]),
dbg:tpl(Module, Fun, [{'_',[],[{return_trace},{exception_trace}]}]),
ok.
dbgon_port(Module, TcpPort) when is_integer(TcpPort) ->
io:format("Use this command on the node you're tracing (-remsh ...)\n"),
io:format("Use dbg:stop() on target node when done.\n"),
{ok,_} = dbg:tracer(port, dbg:trace_port(ip, TcpPort)),
dbg:p(all,call),
%%dbg:tpl(Module, [{'_',[],[{return_trace}]}]),
dbg:tpl(Module, [{'_',[],[{return_trace},{exception_trace}]}]),
ok.
dbg_ip_trace(TcpPort) ->
io:format("Run on same machine as target but NOT -remsh target-node\n"),
io:format("Use dbg:stop() when done.\n"),
dbg:trace_client(ip, TcpPort).
dbgadd(Module) ->
%%dbg:tpl(Module, [{'_',[],[{return_trace}]}]),
dbg:tpl(Module, [{'_',[],[{return_trace},{exception_trace}]}]),
ok.
dbgadd(Module, Fun) ->
%%dbg:tpl(Module, Fun, [{'_',[],[{return_trace}]}]),
dbg:tpl(Module, Fun, [{'_',[],[{return_trace},{exception_trace}]}]),
ok.
dbgdel(Module) ->
dbg:ctpl(Module),
ok.
dbgdel(Module, Fun) ->
dbg:ctpl(Module, Fun),
ok.
dbgclr() ->
dbg:stop_clear().
dbgoff() ->
dbg:stop().
%% Reload modules that have been modified since last load. From Tobbe
%% Tornqvist, http://blog.tornkvist.org/, "Easy load of recompiled
%% code", which may in turn have come from Serge?
l() ->
[c:l(M) || M <- mm()].
mm() ->
modified_modules().
modified_modules() ->
[M || {M, _} <- code:all_loaded(),
module_modified(M) == true].
module_modified(Module) ->
case code:is_loaded(Module) of
{file, preloaded} ->
false;
{file, Path} ->
CompileOpts =
proplists:get_value(compile, Module:module_info()),
CompileTime = proplists:get_value(time, CompileOpts),
Src = proplists:get_value(source, CompileOpts),
module_modified(Path, CompileTime, Src);
_ ->
false
end.
module_modified(Path, PrevCompileTime, PrevSrc) ->
case find_module_file(Path) of
false ->
false;
ModPath ->
case beam_lib:chunks(ModPath, ["CInf"]) of
{ok, {_, [{_, CB}]}} ->
CompileOpts = binary_to_term(CB),
CompileTime = proplists:get_value(time,
CompileOpts),
Src = proplists:get_value(source, CompileOpts),
not (CompileTime == PrevCompileTime) and
(Src == PrevSrc);
_ ->
false
end
end.
find_module_file(Path) ->
case file:read_file_info(Path) of
{ok, _} ->
Path;
_ ->
%% may be the path was changed
case code:where_is_file(filename:basename(Path)) of
non_existing ->
false;
NewPath ->
NewPath
end
end.
%% Reload all modules, regardless of age.
la() ->
FiltZip = lists:filter(
fun({_Mod, Path}) when is_list(Path) ->
case string:str(Path, "/kernel-") +
string:str(Path, "/stdlib-") of
0 -> true;
_ -> false
end;
(_) -> false
end, code:all_loaded()),
{Ms, _} = lists:unzip(FiltZip),
lists:foldl(fun(M, Acc) ->
case shell_default:l(M) of
{error, _} -> Acc;
_ -> [M|Acc]
end
end, [], Ms).
my_tracer() ->
dbg:tracer(process, {fun my_dhandler/2, user}).
my_dhandler(TraceMsg, Acc) ->
dbg:dhandler(filt_state_from_term(TraceMsg), Acc).
filt_state_from_term(T) when is_tuple(T), element(1, T) == state ->
sTatE;
filt_state_from_term(T) when is_tuple(T), element(1, T) == chain_r ->
cHain_R;
filt_state_from_term(T) when is_tuple(T), element(1, T) == g_hash_r ->
g_Hash_R;
filt_state_from_term(T) when is_tuple(T), element(1, T) == hash_r ->
hAsh_R;
filt_state_from_term(T) when is_tuple(T) ->
list_to_tuple(filt_state_from_term(tuple_to_list(T)));
filt_state_from_term([H|T]) ->
[filt_state_from_term(H)|filt_state_from_term(T)];
filt_state_from_term(X) ->
X.
%% ===================================================================
%% Event Viewer
%% ===================================================================
etv() ->
etv("").
etv(Title) ->
et_viewer:start([
{title, Title},
{trace_global, true},
{trace_pattern, {et, max}}
]).
%% ===================================================================
%% Pid generic
%% ===================================================================
-type reg_name() :: atom().
-type process() :: pid() | reg_name() | pos_integer().
-spec pid_do(process(), fun((pid()) -> any())) -> any().
pid_do(Process, Fun) when is_integer(Process) ->
try c:pid(0, Process, 0) of
Pid -> pid_do(Pid, Fun)
catch
_:_ -> pid_do(bad_pid, Fun)
end;
pid_do(Process, Fun) when is_pid(Process) ->
Fun(Process);
pid_do(Process, Fun) when is_atom(Process), Process =/= undefined ->
Pid = whereis(Process),
pid_do(Pid, Fun);
pid_do(_, _) ->
io:format("error: invalid process~n").
%% ===================================================================
%% Process status/state/info
%% ===================================================================
-spec status(process()) -> any().
status(Process) ->
status(Process, print).
-spec status(process(), fetch | print) -> any().
status(Process, Action) ->
pid_do(Process,
fun(Pid) ->
try sys:get_status(Pid) of
Status ->
case Action of
fetch ->
Status;
print ->
io:format("~p~n", [Status])
end
catch
Exc:Err ->
io:format("~p: ~p~n", [Exc, Err])
end
end
).
-spec state(process()) -> any().
state(Process) ->
state(Process, print).
-spec state(process(), fetch | print) -> any().
state(Process, Action) ->
pid_do(Process,
fun(Pid) ->
try sys:get_status(Pid) of
Status ->
case fetch_state(Status) of
{ok, State} ->
case Action of
fetch ->
State;
print ->
print_state(State)
end;
{error, Error} ->
io:format("error: ~p~n", [Error])
end
catch
Exc:Err ->
io:format("~p: ~p~n", [Exc, Err])
end
end
).
print_state({data, State}) ->
io:format("~p~n", [State]);
print_state({data, StateName, StateData}) ->
io:format("~p~n~p~n", [StateName, StateData]).
fetch_state({status,_,{module,gen_server},[_,_,_,_,[_,_,{data,Data}]]}) ->
State = proplists:get_value("State", Data),
{ok, {data, State}};
fetch_state({status,_,{module,gen_fsm},[_,_,_,_,[_,{data,Data1},{data,Data2}]]}) ->
StateName = proplists:get_value("StateName", Data1),
StateData = proplists:get_value("StateData", Data2),
{ok, {data, StateName, StateData}};
fetch_state(_) ->
{error, not_implemented}.
-spec pi(process()) -> any().
pi(Process) ->
pi(Process, print).
-spec pi(process(), fetch | print) -> any().
pi(Process, Action) ->
pid_do(Process,
fun(Pid) ->
try process_info(Pid) of
Info ->
case Action of
fetch ->
Info;
print ->
io:format("~p~n", [Info])
end
catch
Exc:Err ->
io:format("~p: ~p~n", [Exc, Err])
end
end
).
%% ===================================================================
%% Kill process
%% ===================================================================
-type reason() :: term().
-spec kill(process(), reason()) -> any().
kill(Process, Reason) ->
pid_do(Process, fun(Pid) -> erlang:exit(Pid, Reason) end).
-spec kill(pid() | reg_name() | pos_integer()) -> any().
kill(Process) ->
kill(Process, kill).
%% ===================================================================
%% OS command
%% ===================================================================
os(Command) ->
Res = os:cmd(Command),
io:format("~s~n", [Res]).
%% ===================================================================
%% Lager
%% ===================================================================
lager(Level) ->
lager:set_loglevel(lager_console_backend, Level).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment