user_default

Martin Bjorklund <>
Fri Feb 25 22:22:25 CET 2000


  o  did you like the old c() better?
  o  would you like to be able to use bt() in production environments?
  o  tired of writing loops on funs in the shell to get info on ports?
  o  ever wondered why you can't use registered names in
     process_info()?

... then put this module in your code path, e.g. in your .erlang file,
and try i(), pi(), etc in the shell.


?mbj
-------------- next part --------------
%%%----------------------------------------------------------------------
%%% File    : user_default.erl
%%% Author  : Martin Bjorklund <>
%%% Purpose : Nice shell features.
%%% Created : 24 Feb 2000 by Martin Bjorklund <>
%%%----------------------------------------------------------------------

-module(user_default).
%% We Don't Believe in Authors
%% -author('').  

-export([i/0, i/1, i/3, ni/0, ci/0, ci/3, cni/0, bt/1, bt/3]).
-export([pi/0, pi/1, pi2/0]).

-import(lists, [filter/2, foreach/2, flatmap/2]).

-define(mbj, /martin).

%% Good ol' i() but includes zooombie support
i() -> i1(processes()).
ni() -> i1(all_procs()).


i(Pid) when pid(Pid) -> pinfo(Pid);
i(Name) when atom(Name) ->
    case whereis(Name) of
	undefined -> undefined;
	Pid -> i(Pid)
    end.

i(X,Y,Z) -> pinfo(c:pid(X,Y,Z)).


%% If you like the new one
ci() ->
    c:i().

ci(X,Y,Z) ->
    c:i(X,Y,Z).

cni() ->
    c:ni().


%% Code modified from c.erl
i1(Ps) ->
    Alive = filter(fun palive/1, Ps),
    i2(Alive),
    case filter(fun pzombie/1, Ps) of
	[] ->
	    ok;
	Zombies ->
	    %% Zombies is not the same as Ps-Alive, since the remote
	    %% process that fetched Ps is included among Alive, but has
	    %% exited (for ni/0).
	    io:format("\nDead processes:\n"),
	    i2(Zombies)
    end.

i2(Ps) ->
    iformat("Pid", "Initial Call", "Current Function", "Reds", "Msgs"),
    {R,M} = lists:foldl(fun display_info/2, {0,0}, Ps),
    iformat("Total", "", "", io_lib:write(R), io_lib:write(M)).

palive(Pid) ->
    case pinfo(Pid, status) of
	undefined         -> false;
	{status, exiting} -> false;
	_                 -> true
    end.

pzombie(Pid) ->
    case pinfo(Pid, status) of
	undefined         -> false;
	{status, exiting} -> true;
	_                 -> false
    end.

pinfo(Pid) ->
    case is_alive() of
	true -> rpc:call(node(Pid), erlang, process_info, [Pid]);
	false -> process_info(Pid)
    end.

pinfo(Pid, Item) ->
    case is_alive() of
	true -> rpc:call(node(Pid), erlang, process_info, [Pid, Item]);
	false -> process_info(Pid, Item)
    end.

all_procs() ->
    case is_alive() of
	true -> flatmap(fun (N) -> rpc:call(N, erlang, processes, []) end,
			[node() | nodes()]);
	false -> processes()
    end.

display_info(Pid, {R,M}) ->
    case pinfo(Pid) of
	undefined ->
	    {R, M};
	Info ->
	    Call = initial_call(Info),
	    Curr = fetch(current_function, Info),
	    Reds = fetch(reductions, Info),
	    LM = fetch(message_queue_len, Info),
	    iformat(io_lib:write(Pid),
		    mfa_string(Call),
		    mfa_string(Curr),
		    io_lib:write(Reds),
		    io_lib:write(LM)),
	    {R+Reds, M+LM}
    end.

%% We can do some assumptions about the initial call.
%% If the initial call is proc_lib:init_p/5 we can find more information
%% by calling the function proc_lib:translate_initial_call/1.
initial_call(Info)  ->
    case fetch(initial_call, Info) of
	{proc_lib, init_p, 5} ->
	    proc_lib:translate_initial_call(Info);
	ICall ->
	    ICall
    end.

mfa_string({M, F, A}) ->
    io_lib:format("~w:~w/~w", [M, F, A]);
mfa_string(X) ->
    io_lib:write(X).

fetch(Key, Info) ->
    case lists:keysearch(Key, 1, Info) of
	{value, {_, Val}} -> Val;
	false -> 0
    end.

iformat(A1, A2, A3, A4, A5) ->
    io:format("~-12s ~-23s ~-23s ~12s ~4s\n", [A1,A2,A3,A4,A5]).


%% Port info
%% I don't really know which info is most relevent, so I included
%% both pi() and pi2().
pi() ->
    piformat("Id", "Name", "Connected", "Initial Call", "Current Function"),
    do_pi(fun(Info) ->
		  Pid = fetch(connected, Info),
		  {ICall, Curr} =
		      case pinfo(Pid) of
			  undefined ->
			      {[], []};
			  ProcInfo ->
			      {initial_call(ProcInfo),
			       fetch(current_function, ProcInfo)}
		      end,
		  Id = fetch(id, Info),
		  Name = fetch(name, Info),
		  piformat(io_lib:write(Id), 
			   Name,
			   io_lib:write(Pid),
			   mfa_string(ICall),
			   mfa_string(Curr))
	  end).
	     
piformat(A1, A2, A3, A4, A5) ->
    io:format("~-6s ~-10s ~-12s ~-23s ~-23s\n", [A1,A2,A3,A4,A5]).

pi2() ->
    pi2format("Id", "Name", "Connected", "Recv", "Sent"),
    do_pi(fun(Info) ->
		  Id = fetch(id, Info),
		  Name = fetch(name, Info),
		  Pid = fetch(connected, Info),
		  Recv = fetch(input, Info),
		  Sent = fetch(output, Info),
		  pi2format(io_lib:write(Id), 
			   Name,
			   io_lib:write(Pid),
			   io_lib:write(Recv),
			   io_lib:write(Sent))
	  end).

pi2format(A1, A2, A3, A4, A5) ->
    io:format("~-6s ~-20s ~-12s ~-10s ~-10s\n", [A1,A2,A3,A4,A5]).

do_pi(Print) ->
    Ps = erlang:ports(),
    foreach(
      fun(P) ->
	      case erlang:port_info(P) of
		  undefined ->
		      ok;
		  Info ->
		      Print(Info);
		  _ ->
		      ok
	      end
      end, erlang:ports()).


pi(Id) ->
    pi(erlang:ports(), Id).

pi([P | Ps], Id) ->
    case erlang:port_info(P, id) of
	{id, Id} ->
	    erlang:port_info(P);
	_ ->
	    pi(Ps, Id)
    end;
pi([], _Id) ->
    undefined.


%% Doesn't do process_display, which means it can be used when
%% remotely connecting to a node.
bt(Pid) when pid(Pid) ->
    case pinfo(Pid, backtrace) of
	{backtrace, Bin} ->
	    io:format("~s\n", [binary_to_list(Bin)]);
	_ ->
	    undefined
    end;
bt(Name) when atom(Name) ->
    case whereis(Name) of
	undefined -> undefined;
	Pid -> bt(Pid)
    end.


bt(X,Y,Z) ->
    bt(c:pid(X,Y,Z)).


More information about the erlang-questions mailing list