Known BEAM memory leak ? Profiling tools ?

Ulf Wiger etxuwig@REDACTED
Fri Jul 14 09:41:31 CEST 2000


On Fri, 14 Jul 2000, Jon Holdsworth wrote:

>To the erlang-questions people:
>
>Erlang running under Linux Redhat 6.1 on Intel and Solaris 2.7 on Sparc,
>the BEAM program has been seen to apparently leak memory after being
>left running for some time.  It is possible that this 'memory leak' is
>in our own code, but it has not revealed itself yet.
>
>We have looked at the erlang processes running, heap and stack thereof,
>and also the ETS and Mnesia tables, and there appears to be nothing
>amiss there, pointing to the virtual machine engine, but we are also
>aware of how unlikely a fault in the engine is.
>
>The worst-case leak seen so far caused beam to expand to about 200 MB,
>as reported by 'top'.

What is the resident ("RES") size reported by 'top'?

It is not that uncommon that BEAM will allocate tons of memory to meet
a transient need (and it tends to allocate about 3X the actual
size of the data). Once this has happened, 'top' will report the
all-time high. This is not a problem, as long as you have enough
physical memory to make it through the transient without plunging into
swap. 

For example, I have an Erlang-based web server which reports 

SIZE   RES  STATE   TIME    CPU COMMAND
1003M  400M sleep  49.7H  0.00% beam

In other words, beam has once exceeded 1 GB (!), but is now down to
using 400 MB of resident memory. Some live data might well be in swap,
but I think the majority of the 600 MB difference is memory marked as
free, but not reclaimed by the OS (This particular SPARC has 1024 MB
RAM, BTW.)


>Has anything like this been encountered before and if so is there a
>known solution ?

If this is the problem, and you want to fix it, try doing this:
- Use something like vmstat to find out the behaviour of your system
  over time. You can write a looping script that prints the memory
  statistics to a log file.

- If you can identify a situation where beam grows significantly, 
  try to reproduce it, and run a GC trace at the same time.
  A colleague of mine, Mats Cronqvist, has written some utilities
  for this purpose (attached - the source code is the documentation)

- If you can identify a process or some processes that trigger many
  (large) garbage collections, check the functions in which GC was 
  triggered, and look at the code. Constructs to look out for are 
  those that build large amounts of live data in a short time.

  
/Uffe
-- 
Ulf Wiger                                    tfn: +46  8 719 81 95
Network Architecture & Product Strategies    mob: +46 70 519 81 95
Ericsson Telecom AB,              Datacom Networks and IP Services
Varuvägen 9, Älvsjö,                    S-126 25 Stockholm, Sweden

-------------- next part --------------
%%%----------------------------------------------------------------------
%%% File    : funt.erl
%%% Author  : Mats Cronqvist <etxmacr@REDACTED>
%%% Purpose : 
%%% Created :  8 Feb 2000 by Mats Cronqvist <etxmacr@REDACTED>
%%%----------------------------------------------------------------------

-module(funt).
-author('etxmacr@REDACTED').

-behaviour(gen_server).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
%%%----------------------------------------------------------------------
%%% Function     : 
%%% Author       : Mats Cronqvist <etxmacr@REDACTED>
%%% Arguments    : 
%%% Return value : 
%%% Description  : 
%%%----------------------------------------------------------------------
-export([start/0, start/1]).
-export([stop/0]).
-export([add/0, add/1, add/2, add/3]).
-export([sub/0, sub/1, sub/2, sub/3]).
-export([info/0]).
-export([sleep/0]).

-define(FLAGS0, [set_on_spawn, timestamp, procs, garbage_collection]).
-define(FLAGS, [send, 'receive', bifs, call, running, garbage_collection, procs]).
-define(TAGS, ['receive', send, send_to_non_existing_process, call, return, spawn, 
	       exit, link, unlink, getting_linked, in, out, gc_start, gc_end]).
-record(ld, {gc_start = [],
	     gc_file,
	     gc_trig = 150000,
	     logging = false}).
%%%----------------------------------------------------------------------
%%% Function     : gen_server callback funcs
%%% Author       : Mats Cronqvist <etxmacr@REDACTED>
%%% Arguments    : 
%%% Return value : 
%%% Description  : 
%%%----------------------------------------------------------------------
init(Args) -> 
    {ok, do_init(Args)}.
handle_call(Data, {Pid, _}, LD) -> 
    {reply, ok, do_call(Data, Pid, LD)}.
handle_cast(stop, LD) ->
    {stop, normal, LD};
handle_cast(Data, LD) -> 
    {noreply, do_cast(Data, LD)}.
handle_info(Data, LD) -> 
    erlang:yield(),
    {noreply, do_info(Data, LD)}.
terminate(Reason, LD) -> 
    ok.

%%%----------------------------------------------------------------------
%%% Function     : exported API funcs
%%% Author       : Mats Cronqvist <etxmacr@REDACTED>
%%% Arguments    : 
%%% Return value : 
%%% Description  : 
%%%----------------------------------------------------------------------
start() ->
    start(processes()).
start(Arg) when atom(Arg) ->
    start([Arg]);
start(Args) when list(Args) ->
    As = [apid(A) || A <- Args],
    gen_server:start({local, ?MODULE}, ?MODULE, As, []).

stop() ->
    gen_server:cast(?MODULE, stop).

sleep() ->
    gen_server:cast(?MODULE, sleep).

info() ->
    print_info().

add() ->
    add(all).
add(Target) ->
    add(Target, screen).
add(Target, Consumer) ->
    add(Target, Consumer, ?FLAGS).
add(Target, Consumer, Flag) when atom(Flag) ->
    add(Target, Consumer, [Flag]);
add(Target, Consumer, Flags) when list(Flags)->
    case {Flags--?FLAGS, chkt(Target), chkc(add, Consumer)} of
	{Error, _, _} when Error /= [] ->
	    ok = io:fwrite("~p: bad flag(s): ~p~n", [?MODULE, Error]);
	{_, error, _} ->
	    ok = io:fwrite("~p: bad target: ~p~n", [?MODULE, Target]);
	{_, _, error} ->
	    ok = io:fwrite("~p: bad consumer: ~p~n", [?MODULE, Consumer]);
	_ ->
	    case whereis(?MODULE) of
		undefined -> start();
		_ -> ok
	    end,
	    gen_server:call(?MODULE, {add, Target, Consumer, Flags}, infinity)
    end.

sub() ->
    sub(all).
sub(pending) ->
    gen_server:call(?MODULE, {sub, pending}, infinity);
sub(Target) ->
    sub(Target, all).
sub(Target, Consumer) ->
    sub(Target, Consumer, ?FLAGS).
sub(Target, Consumer, Flag) when atom(Flag) ->
    sub(Target, Consumer, [Flag]);
sub(Target, Consumer, Flags) when list(Flags)->
    case {Flags--?FLAGS, chkt(Target), chkc(sub, Consumer)} of
	{Error, _, _} when Error /= [] ->
	    ok = io:fwrite("~p: bad flag(s): ~p~n", [?MODULE, Error]);
	{_, error, _} ->
	    ok = io:fwrite("~p: bad target: ~p~n", [?MODULE, Target]);
	{_, _, error} ->
	    ok = io:fwrite("~p: bad consumer: ~p~n", [?MODULE, Consumer]);
	_ ->
	    gen_server:call(?MODULE, {sub, Target, Consumer, Flags}, infinity)
    end.
%%%----------------------------------------------------------------------
%%% Function     : action!
%%% Author       : Mats Cronqvist <etxmacr@REDACTED>
%%% Arguments    : 
%%% Return value : 
%%% Description  : returns LD
%%%----------------------------------------------------------------------
do_init(Args) ->
    ets_new(),
    [on(P) || P <- (Args--[self()])],
    {ok, FD} = file:open(os:getenv("HOME")++"/gc_log", [write]),
    #ld{gc_file = FD}.

do_cast(Data, LD) ->
    ok = io:fwrite("~p:unknown cast ~p ~p~n", [?MODULE, Data, LD]),
    LD.

do_call({add, Target, Consumer, Flags}, Pid, LD) ->
    case apid(Target) of
	error -> do_add(Target, Consumer, Flags);
	P -> do_add(P, Consumer, Flags)
    end,
    set_logging(LD);
do_call({sub, pending},  Pid, LD) ->
    do_sub(pending),
    set_logging(LD);
do_call({sub, Target, Consumer, Flags}, Pid, LD) ->
    case apid(Target) of
	error -> do_sub(Target, Consumer, Flags);
	P -> do_sub(P, Consumer, Flags)
    end,
    set_logging(LD);
do_call(sleep, Pid, LD) ->
    ok = io:fwrite("~p:~p: sleeping~n", [?MODULE, ?LINE]),
    receive
    after 20000 -> ok
    end,
    ok = io:fwrite("~p:~p: waking~n", [?MODULE, ?LINE]),
    LD;
do_call(Data, Pid, LD) ->
    ok = io:fwrite("~p:unknown call ~p ~p ~p~n", [?MODULE, Data, Pid, LD]),
    LD.

do_info({trace,'receive',Pid,timeout,_}, LD)->
%%%    ets_ins({{miss, reg(Pid), receive_timeout}, process_info(Pid)}),
    LD;
do_info({trace, Pid, Tag = gc_start, Info, Now} = Data, LD) ->
    log(Data, LD),
    LD#ld{gc_start = {Now, Tag, Pid, Info}};
do_info({trace, Pid, Tag = gc_end, Info, Now} = Data, LD) ->
    log(Data, LD),
    gc_log(LD, process_info(Pid), reg(Pid), LD#ld.gc_start, 
	   {Now, Tag, Pid, Info}),
    LD#ld{gc_start = []};
do_info({trace, PidP, spawn, PidC, _} = Data, LD) ->
    log(Data, LD),
    do_add_child(PidC, PidP),
    do_add_pending(PidC),
    LD;
do_info({trace, Pid, exit, _, _} = Data, LD) ->
    log(Data, LD),
    do_sub(Pid, all, ?FLAGS),
    LD;
do_info({?MODULE, gc_trig, Data}, LD) ->
    LD#ld{gc_trig = Data};
do_info(Data, LD) ->
    log(Data, LD),
    LD.

%%%----------------------------------------------------------------------
%%% Function     : lib-like funcs
%%% Author       : Mats Cronqvist <etxmacr@REDACTED>
%%% Arguments    : 
%%% Return value : 
%%% Description  : 
%%%----------------------------------------------------------------------
set_logging(LD) ->
    case ets_siz() of
	0 -> LD#ld{logging = false};
	_ -> LD#ld{logging = true}
    end.

do_add_child(PidC, PidP) ->
    on(PidC),
    [do_add(PidC, Cons, get_flags(PidP, Cons)) || Cons <- get_cons(PidP)],
    ok.

do_add_pending(Pid) ->
    K = {pending, process_info(Pid,initial_call)},
    case ets_get(K) of
	[] -> ok;
	{Cons, Flags} -> 
	    do_add(Pid, Cons, Flags),
	    ets_del(K)
    end.

do_add({pending, InitialCall} = K, Cons, Flags) ->
    ets_ins({{pending, {initial_call, InitialCall}}, {Cons, Flags}});
do_add(all, Cons, Flags) ->
    [do_add(Pid, Cons, Flags) || Pid <- processes()];
do_add(Pid, screen, Flags) ->
    do_add(Pid, {file, screen, standard_io}, Flags);
do_add(Pid, Fil, Flags) when list(Fil) ->
    {FD, File} = open(Fil),
    do_add(Pid, {file, File, FD}, Flags);
do_add(Pid, Cons, Flags) when pid(Pid)->
    Fs = ets_sel({{flags, Pid, '_'}, '$1'}),
    case (Flags--?FLAGS0)--Fs of
	[] ->ok;
	F -> 
	    ok = io:fwrite("~p:~p: added ~p to ~p~n", [?MODULE, ?LINE, F, reg(Pid)]),
	    catch trace(Pid, true, F)
    end,
    ets_add({{flags, Pid, Cons}, Flags--?FLAGS0}),
    Tags = get_tags(Flags),
    ets_add({{pids, Cons}, Pid}),
    [ets_add({{cons, Pid, Tag}, Cons}) || Tag <- Tags].

do_sub(pending) ->
    [ets_del({pending, K}) || [K] <- ets_mch({{pending, '$1'}, '_'})].
do_sub(Targ, all, Flags) ->
    [do_sub(Targ, Cons, Flags) || Cons <- get_cons(Targ)];
do_sub(all, Cons, Flags) ->
    [do_sub(Pid, Cons, Flags) || Pid <- ets_get({pids, Cons})];
do_sub(Pid, screen, Flags) ->
    do_sub(Pid, {file, screen, standard_io}, Flags);
do_sub(Pid, FD, Flags) when list(FD) ->
    do_sub(Pid, {file, get_file(FD), FD}, Flags);
do_sub(Pid, Cons, Flags) when list(Flags) ->
    [do_sub(Pid, Cons, Flag) || Flag <- Flags];
do_sub(Pid, Cons, Flag) ->
    case get_con(Pid, Flag)--[Cons] of
	[] ->
	    case lists:member(Flag, ?FLAGS0) of
		false -> catch trace(Pid, false, [Flag]);
		true -> ok
	    end;
	X -> ok
    end,
    case ets_sub({{flags, Pid, Cons}, Flag}) of
	[] ->
	    case ets_sub({{pids, Cons}, Pid}) of
		[] -> close(Cons);
		_ -> ok
	    end;
	_ -> ok
    end,
    Tags = get_tags(Flag)--get_tags(ets_get({flags, Pid, Cons})), 
    [ets_sub({{cons, Pid, Tag}, Cons}) || Tag <- Tags].

get_con(Pid, Flag) ->
    Fs = ets_mch({{flags, Pid, '$1'}, '$2'}),
    [Con || [Con, Flist] <- Fs, lists:member(Flag, Flist)].

get_cons(all) ->
    ets_sel({{pids, '$1'}, '_'});
get_cons(Pid) ->
    ets_sel({{flags, Pid, '$1'}, '_'}).

get_cons(Pid, Tag) when atom(Tag) ->
    get_cons(Pid, [Tag]);
get_cons(Pid, Tags) ->
    get_cons(Pid, Tags, []).
get_cons(Pid, [], O) ->
    uniq(O);
get_cons(Pid, [Tag|T], O) ->
    get_cons(Pid, T, ets_get({cons, Pid, Tag})++O).

get_flags(Tags) ->
    [Flag || Flag <- ?FLAGS, intersect(Tags, get_tags(Flag)) /= []].
get_flags(Pid, Cons) ->
    ets_get({flags, Pid, Cons}).

get_tags(Flags) when list(Flags)-> 
    uniq(lists:flatten([get_tags(Flag) || Flag <- Flags]));
get_tags(Flag) -> 
    case Flag of 
	send ->
	    [send, send_to_non_existing_process];
	'receive' ->
	    ['receive'];
	procs ->
	    [spawn, exit, link, unlink, getting_linked];
	bifs ->
	    [call];
	call ->
	    [call, return];
	running ->
	    [in, out];
	garbage_collection ->
	    [gc_start, gc_end];
	_ -> []
    end.

get_tags(Pid, Cons) ->
    lists:flatten(ets_mch({{cons, Pid, '$1'}, [Cons]})).

log(Data, LD) when record(LD, ld) ->
    case LD#ld.logging of
	true -> log(Data);
	false -> ok
    end;
log({_, Tag, Pid, _} = D0, Data) ->
    case ets_get({cons, Pid, Tag}) of
	[] -> %%ets_upd({miss, reg(Pid), Tag});
	    ok;%%ets_ins({{miss, reg(Pid), Tag}, process_info(Pid)});
	Cons ->
	    [do_log(D0, Data, Con) || Con <- Cons]
    end.
log({trace, Tag, Info, Pid, Proc, Now} = Data) ->
    log({Now, Tag, Pid, {Proc, Info}}, Data);
log({trace, 'receive' = Tag, Pid, Info, Now} = Data) ->
    log({Now, Tag, Pid, Info}, Data);
log({trace, Pid, Tag, Info, Now} = Data) ->
    log({Now, Tag, Pid, Info}, Data).
do_log(D0, Data, {file, _, FD}) ->
    print_log(D0, FD);
do_log(D0, Data, Fun) when function(Fun) ->
    case Fun(Data) of
	{true, X} -> ok = io:fwrite("~p~n", [X]);
	true -> print_log(D0, standard_io);
	_ -> ok
    end;
do_log(D0, Data, Pid) when pid(Pid) ->
    Pid ! {Data}.

print_log({Now, send, Pid, {Pid, {dbgm_wrap, {M, L, F, A}}}}, standard_io) ->
    Form = "DBGM ~s - <~p> - ~p.erl:~p - ~p/~p~n",
    ok = io:fwrite(Form, [ntform(Now), reg(Pid), M, L, F, A]);
print_log({Now, Tag, Pid, Info}, standard_io) ->
    Form = "~s, ~w, ~w, ~w~n",
    ok = io:fwrite(Form, [ntform(Now), Tag, reg(Pid), Info]);
print_log({Now, Tag, Pid, Info}, FD) ->
    disk_log:alog(FD, {Now, Tag, reg(Pid), Info}).

gc_log(LD, Pinfo, Name, {Now0, _, _, Info0}, {Now1, _, _, Info1}) ->
    Gci = gci(Info0, Info1),
    case element(2, hd(Gci)) of
	X when X > LD#ld.gc_trig ->
	    Del = lists:duplicate(70,$%),
	    ok = io:fwrite(LD#ld.gc_file, "~s~n% ~s  (~w us)~n~w.~n~p.~n~p.~n~s~n",
			   [Del, ntform(Now1), ntform(Now0, Now1), to_atom(Name), 
			    Gci, filt(Pinfo), Del]);
	_ ->
	    ok
    end.

print_info() ->
    ok = io:fwrite("~-20w:~-30w:~w~n", [target, consumer, flags]),
    [print_info(D) || D <- ets_mch({{flags,'$1','$2'},'$3'})],
    ok.
print_info([Targ, {file, Cons, Pid}, Flag]) when pid(Pid) ->
    print_info([Targ, list_to_atom(os:getenv("HOME")++"/"++Cons), Flag]);
print_info([Targ, {file, File, _}, Flag]) when list(File) ->
    print_info([Targ, list_to_atom(File), Flag]);
print_info([Targ, {file, File, _}, Flag]) when atom(File) ->
    print_info([Targ, File, Flag]);
print_info([Targ, Cons, Flag]) ->
    ok = io:fwrite("~-20w:~-30w:~w~n", [reg(Targ), Cons, Flag]).

filt([{group_leader, _}|T]) -> filt(T);
filt([{dictionary, _}|T]) -> filt(T);
filt([{messages, _}|T]) -> filt(T);
filt([{links, _}|T]) -> filt(T);
filt([H|T]) -> [H|filt(T)];
filt([]) -> [];
filt(X) -> X.

gci(A, B) ->
    gci(A, B, [], 0, 0).
gci([], [], O, Tot0, Tot1) ->
    [{tot0, Tot0}, {tot1, Tot1}|O];
gci([{T,D0}|T0], [{T,D1}|T1], O, Tot0, Tot1) ->
    gci(T0, T1, [{T, {D0, D1, D1-D0}}|O], Tot0+D0, Tot1+D1).

to_atom(X) when pid(X) ->
    list_to_atom(pid_to_list(X));
to_atom(X) when atom(X) ->
    X.

on(error) -> ok;
on(P) ->
    case apid(P) of
	error -> error;
	Pid -> catch trace(Pid, true, ?FLAGS0)
    end.

apid(Pid) when pid(Pid) -> Pid;
apid(Atom) when atom(Atom) ->
    case whereis(Atom) of
	undefined -> error;
	Pid -> Pid
    end;
apid(_) -> error.

reg(Pid) ->
    case catch process_info(Pid, registered_name) of
	{registered_name, R} -> R;
	_ -> Pid
    end.

close({file, File, standard_io}) -> ok;
close({file, File, FD}) when pid(FD) ->	file:close(FD);
close({file, File, FD}) -> disk_log:close(FD);
close(_) -> ok.
open(FD) ->
    case get_file(FD) of
	[] ->
%%%	    {ok, FD} = file:open(os:getenv("HOME")++"/"++File, [write]),
	    File = os:getenv("HOME")++"/"++FD++".LOG",
	    Opts = [{name, FD}, {file, File}, {type, halt}, 
		    {size, infinity}, {mode, read_write}],
	    case disk_log:open(Opts) of
		{ok, FD} -> 
		    ok = io:fwrite("~p:~p: opened log : ~p~n", [?MODULE, ?LINE, Opts]),
		    {FD, File};
		X -> 
		    ok = io:fwrite("~p:~p: error opening ~p: ~p~n", 
				   [?MODULE, ?LINE, File, X]),
		    exit(bad_file)
	    end;
	File -> {FD, File}
    end.
get_file(FD) ->
    case ets_mch({{pids, {file, '$1', FD}}, '_'}) of
	[] -> [];
	[[File]] -> File
    end.

chkt({pending,{_,_,_}}) -> ok;
chkt(X) ->
    case {apid(X), X == all} of
	{error, false} -> error;
	_ -> ok
    end.

chkc(_, X) when function(X) -> ok;
chkc(add, X) ->
    case {apid(X), X == screen, catch list_to_binary(X)} of
	{error, false, {'EXIT', _}} -> error;
	_ -> ok
    end;
chkc(sub, X) ->
    case {apid(X), X == all, X == screen, catch list_to_binary(X)} of
	{error, false, false, {'EXIT', _}} -> error;
	_ -> ok
    end.

ntform({Msec0, Sec0, Usec0}, {Msec, Sec, Usec}) ->
    (Msec-Msec0)*(1000*1000*1000*1000)+(Sec-Sec0)*1000*1000+(Usec-Usec0).
ntform({Msec, Sec, Usec} = Now) ->
    T = tuple_to_list(element(2,calendar:now_to_datetime(Now)))++[Usec],
    io_lib:fwrite("~2.2.0w:~2.2.0w:~2.2.0w.~6.6.0w", T).

intersect(L1, L2) ->
    [E || E <- L1, lists:member(E, L2)].
union(L1, L2) ->
    uniq(L1++L2).
    
uniq(List) ->
    uniq(lists:sort(List), []).
uniq([], _) ->
    [];
uniq([A], OutList) ->
    [A|OutList];
uniq([A, A], OutList) ->
    [A|OutList];
uniq([A, A|InList], OutList) ->
    uniq([A|InList], OutList);
uniq([A|InList], OutList) ->
    uniq(InList, [A|OutList]).

ets_new() ->
    ets_new(?MODULE).
ets_new(Tab) ->
    catch ets:delete(Tab),
    ets:new(Tab, [named_table, ordered_set]).
ets_get(Key) ->
    case ets_lup(?MODULE, Key) of
	[{_, A}] -> A;
	[X] -> X;
	Y -> Y
    end.    
ets_lup(Tab, Key) ->
    case catch ets:lookup(Tab, Key) of
	{'EXIT', _} ->[];
	R -> R
    end.
ets_ins(Rec) ->
    ets:insert(?MODULE, Rec),
    element(2, Rec).
ets_del(Key) ->
    ets:delete(?MODULE, Key).
ets_add({Key, List}) when list(List) -> 
    ets_ins({Key, uniq(ets_get(Key)++List)});
ets_add({Key, X}) ->
    ets_add({Key, [X]}).
ets_sub({Key, List}) when list(List)->
    case Data = ets_get(Key)--List of
	[] ->
	    ets_del(Key);
	NewList ->
	    ets_ins({Key, NewList})
    end,
    Data;
ets_sub({Key, X}) ->
    ets_sub({Key, [X]}).
ets_sel(Pat) ->
    uniq(lists:flatten(ets_mch(Pat))).
ets_mch(Pat) ->
    ets:match(?MODULE, Pat).
ets_siz() ->
    ets:info(?MODULE, size).
ets_upd(Key) ->
    ets_upd(?MODULE, Key, 1).
ets_upd(Tab, Key, Inc) ->
    case catch ets:update_counter(Tab, Key, Inc) of
        {'EXIT', _ } -> ets:insert(Tab, {Key, Inc});
        _ -> ok
    end.
-------------- next part --------------
%%%----------------------------------------------------------------------
%%% File    : gc_scan.erl
%%% Author  : Mats Cronqvist <etxmacr@REDACTED>
%%% Purpose : 
%%% Created : 15 Feb 2000 by Mats Cronqvist <etxmacr@REDACTED>
%%%----------------------------------------------------------------------

-module(gc_scan).
-author('etxmacr@REDACTED').

-export([go/1]).

go(File) ->
    ets_new(),
    {ok, T} = file:consult(File),
    dogo(T),
    rep().

rep() ->
    L = ets_mch({{info, '$1'}, {hw, '$2'}, {'$3', '_', '_'}}),
    R = [{P, M, ets_get({gc, P}), HW, ets_get({hw, P})} || [P, HW, M] <- L],
    {lists:reverse(lists:keysort(4, R)),
     [E || E = [A, B, C] <- ets_mch({{call, '$1', '$2'}, '$3'}), C > 1]}.
    
dogo([P, [{tot0, T0}|Gci], Proci|T]) ->
    ets_upd({gc, P}),
    ets_upd({call, P, lks(current_function, Proci)}),
    case ets_lup({info, P}) of
	[] -> ets_ins({{info, P}, {hw, T0}, lks(initial_call, Proci)});
	[{{info, P}, {hw, HW}, I}] when HW < T0 ->
	    ets_ins({{info, P}, {hw, T0}, I}),
	    ets_upd({hw, P});
	_ -> ok
    end,
    dogo(T);
dogo([]) ->
    ok.

lks(Tag, L) ->
    element(2, element(2, lists:keysearch(Tag, 1, L))).

ets_new() ->
    ets_new(?MODULE).
ets_new(Tab) ->
    catch ets:delete(Tab),
    ets:new(Tab, [named_table, ordered_set]).
ets_upd(Key) ->
    ets_upd(?MODULE, Key, 1).
ets_upd(Tab, Key, Inc) ->
    case catch ets:update_counter(Tab, Key, Inc) of
        {'EXIT', _ } -> ets:insert(Tab, {Key, Inc});
        _ -> ok
    end.
ets_ins(Rec) ->
    ets_ins(?MODULE, Rec).
ets_ins(Tab, Rec) ->
    catch ets:insert(Tab, Rec).
ets_lup(Key) ->
    ets_lup(?MODULE, Key).
ets_lup(Tab, Key) ->
    case catch ets:lookup(Tab, Key) of
	{'EXIT', _} ->[];
	R -> R
    end.
ets_get(Key) ->
    case ets_lup(?MODULE, Key) of
	[{_, A}] -> A;
	[X] -> X;
	Y -> Y
    end.    
ets_mch(Pat) ->
    ets:match(?MODULE, Pat).


More information about the erlang-questions mailing list