%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% -module(erlang). -export([apply/2,apply/3,spawn/4,spawn_link/4, spawn_monitor/1,spawn_monitor/3, spawn_opt/2,spawn_opt/3,spawn_opt/4,spawn_opt/5, disconnect_node/1]). -export([spawn/1, spawn_link/1, spawn/2, spawn_link/2]). -export([yield/0]). -export([crasher/6]). -export([fun_info/1]). -export([send_nosuspend/2, send_nosuspend/3]). -export([localtime_to_universaltime/1]). -export([suspend_process/1]). -export([min/2, max/2]). -export([dlink/1, dunlink/1, dsend/2, dsend/3, dgroup_leader/2, dexit/2, dmonitor_node/3, dmonitor_p/2]). -export([delay_trap/2]). -export([set_cookie/2, get_cookie/0]). -export([nodes/0]). -export([concat_binary/1]). -export([list_to_integer/2,integer_to_list/2]). -export([flush_monitor_message/2]). -export([set_cpu_topology/1, format_cpu_topology/1]). -export([await_proc_exit/3]). -export([memory/0, memory/1]). -export([alloc_info/1, alloc_sizes/1]). -deprecated([hash/2]). -deprecated([concat_binary/1]). % Get rid of autoimports of spawn to avoid clashes with ourselves. -compile({no_auto_import,[spawn/1]}). -compile({no_auto_import,[spawn/4]}). -compile({no_auto_import,[spawn_link/1]}). -compile({no_auto_import,[spawn_link/4]}). -compile({no_auto_import,[spawn_opt/2]}). -compile({no_auto_import,[spawn_opt/4]}). -compile({no_auto_import,[spawn_opt/5]}). -export_type([timestamp/0]). -type timestamp() :: {MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer()}. %%-------------------------------------------------------------------------- -spec apply(Fun, Args) -> term() when Fun :: function(), Args :: [term()]. apply(Fun, Args) -> erlang:apply(Fun, Args). -spec apply(Module, Function, Args) -> term() when Module :: module(), Function :: atom(), Args :: [term()]. apply(Mod, Name, Args) -> erlang:apply(Mod, Name, Args). %% Spawns with a fun -spec spawn(Fun) -> pid() when Fun :: function(). spawn(F) when is_function(F) -> spawn(erlang, apply, [F, []]); spawn({M,F}=MF) when is_atom(M), is_atom(F) -> spawn(erlang, apply, [MF, []]); spawn(F) -> erlang:error(badarg, [F]). -spec spawn(Node, Fun) -> pid() when Node :: node(), Fun :: function(). spawn(N, F) when N =:= node() -> spawn(F); spawn(N, F) when is_function(F) -> spawn(N, erlang, apply, [F, []]); spawn(N, {M,F}=MF) when is_atom(M), is_atom(F) -> spawn(N, erlang, apply, [MF, []]); spawn(N, F) -> erlang:error(badarg, [N, F]). -spec spawn_link(Fun) -> pid() when Fun :: function(). spawn_link(F) when is_function(F) -> spawn_link(erlang, apply, [F, []]); spawn_link({M,F}=MF) when is_atom(M), is_atom(F) -> spawn_link(erlang, apply, [MF, []]); spawn_link(F) -> erlang:error(badarg, [F]). -spec spawn_link(Node, Fun) -> pid() when Node :: node(), Fun :: function(). spawn_link(N, F) when N =:= node() -> spawn_link(F); spawn_link(N, F) when is_function(F) -> spawn_link(N, erlang, apply, [F, []]); spawn_link(N, {M,F}=MF) when is_atom(M), is_atom(F) -> spawn_link(N, erlang, apply, [MF, []]); spawn_link(N, F) -> erlang:error(badarg, [N, F]). %% Spawn and atomically set up a monitor. -spec spawn_monitor(Fun) -> {pid(), reference()} when Fun :: function(). spawn_monitor(F) when is_function(F, 0) -> erlang:spawn_opt({erlang,apply,[F,[]],[monitor]}); spawn_monitor(F) -> erlang:error(badarg, [F]). -spec spawn_monitor(Module, Function, Args) -> {pid(), reference()} when Module :: module(), Function :: atom(), Args :: [term()]. spawn_monitor(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> erlang:spawn_opt({M,F,A,[monitor]}); spawn_monitor(M, F, A) -> erlang:error(badarg, [M,F,A]). -spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when Fun :: function(), Options :: [Option], Option :: link | monitor | {priority, Level} | {fullsweep_after, Number :: non_neg_integer()} | {min_heap_size, Size :: non_neg_integer()} | {min_bin_vheap_size, VSize :: non_neg_integer()}, Level :: low | normal | high. spawn_opt(F, O) when is_function(F) -> spawn_opt(erlang, apply, [F, []], O); spawn_opt({M,F}=MF, O) when is_atom(M), is_atom(F) -> spawn_opt(erlang, apply, [MF, []], O); spawn_opt({M,F,A}, O) -> % For (undocumented) backward compatibility spawn_opt(M, F, A, O); spawn_opt(F, O) -> erlang:error(badarg, [F, O]). -spec spawn_opt(Node, Fun, Options) -> pid() | {pid(), reference()} when Node :: node(), Fun :: function(), Options :: [Option], Option :: link | monitor | {priority, Level} | {fullsweep_after, Number :: non_neg_integer()} | {min_heap_size, Size :: non_neg_integer()} | {min_bin_vheap_size, VSize :: non_neg_integer()}, Level :: low | normal | high. spawn_opt(N, F, O) when N =:= node() -> spawn_opt(F, O); spawn_opt(N, F, O) when is_function(F) -> spawn_opt(N, erlang, apply, [F, []], O); spawn_opt(N, {M,F}=MF, O) when is_atom(M), is_atom(F) -> spawn_opt(N, erlang, apply, [MF, []], O); spawn_opt(N, F, O) -> erlang:error(badarg, [N, F, O]). %% Spawns with MFA -spec spawn(Node, Module, Function, Args) -> pid() when Node :: node(), Module :: module(), Function :: atom(), Args :: [term()]. spawn(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) -> spawn(M,F,A); spawn(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) -> case is_well_formed_list(A) of true -> ok; false -> erlang:error(badarg, [N, M, F, A]) end, case catch gen_server:call({net_kernel,N}, {spawn,M,F,A,group_leader()}, infinity) of Pid when is_pid(Pid) -> Pid; Error -> case remote_spawn_error(Error, {no_link, N, M, F, A, []}) of {fault, Fault} -> erlang:error(Fault, [N, M, F, A]); Pid -> Pid end end; spawn(N,M,F,A) -> erlang:error(badarg, [N, M, F, A]). -spec spawn_link(Node, Module, Function, Args) -> pid() when Node :: node(), Module :: module(), Function :: atom(), Args :: [term()]. spawn_link(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) -> spawn_link(M,F,A); spawn_link(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) -> case is_well_formed_list(A) of true -> ok; _ -> erlang:error(badarg, [N, M, F, A]) end, case catch gen_server:call({net_kernel,N}, {spawn_link,M,F,A,group_leader()}, infinity) of Pid when is_pid(Pid) -> Pid; Error -> case remote_spawn_error(Error, {link, N, M, F, A, []}) of {fault, Fault} -> erlang:error(Fault, [N, M, F, A]); Pid -> Pid end end; spawn_link(N,M,F,A) -> erlang:error(badarg, [N, M, F, A]). -spec spawn_opt(Module, Function, Args, Options) -> pid() | {pid(), reference()} when Module :: module(), Function :: atom(), Args :: [term()], Options :: [Option], Option :: link | monitor | {priority, Level} | {fullsweep_after, Number :: non_neg_integer()} | {min_heap_size, Size :: non_neg_integer()} | {min_bin_vheap_size, VSize :: non_neg_integer()}, Level :: low | normal | high. spawn_opt(M, F, A, Opts) -> case catch erlang:spawn_opt({M,F,A,Opts}) of {'EXIT',{Reason,_}} -> erlang:error(Reason, [M,F,A,Opts]); Res -> Res end. -spec spawn_opt(Node, Module, Function, Args, Options) -> pid() | {pid(), reference()} when Node :: node(), Module :: module(), Function :: atom(), Args :: [term()], Options :: [Option], Option :: link | monitor | {priority, Level} | {fullsweep_after, Number :: non_neg_integer()} | {min_heap_size, Size :: non_neg_integer()} | {min_bin_vheap_size, VSize :: non_neg_integer()}, Level :: low | normal | high. spawn_opt(N, M, F, A, O) when N =:= node(), is_atom(M), is_atom(F), is_list(A), is_list(O) -> spawn_opt(M, F, A, O); spawn_opt(N, M, F, A, O) when is_atom(N), is_atom(M), is_atom(F) -> case {is_well_formed_list(A), is_well_formed_list(O)} of {true, true} -> ok; _ -> erlang:error(badarg, [N, M, F, A, O]) end, case lists:member(monitor, O) of false -> ok; true -> erlang:error(badarg, [N, M, F, A, O]) end, {L,NO} = lists:foldl(fun (link, {_, NewOpts}) -> {link, NewOpts}; (Opt, {LO, NewOpts}) -> {LO, [Opt|NewOpts]} end, {no_link,[]}, O), case catch gen_server:call({net_kernel,N}, {spawn_opt,M,F,A,NO,L,group_leader()}, infinity) of Pid when is_pid(Pid) -> Pid; Error -> case remote_spawn_error(Error, {L, N, M, F, A, NO}) of {fault, Fault} -> erlang:error(Fault, [N, M, F, A, O]); Pid -> Pid end end; spawn_opt(N,M,F,A,O) -> erlang:error(badarg, [N,M,F,A,O]). remote_spawn_error({'EXIT', {{nodedown,N}, _}}, {L, N, M, F, A, O}) -> {Opts, LL} = case L =:= link of true -> {[link|O], [link]}; false -> {O, []} end, spawn_opt(erlang,crasher,[N,M,F,A,Opts,noconnection], LL); remote_spawn_error({'EXIT', {Reason, _}}, _) -> {fault, Reason}; remote_spawn_error({'EXIT', Reason}, _) -> {fault, Reason}; remote_spawn_error(Other, _) -> {fault, Other}. is_well_formed_list([]) -> true; is_well_formed_list([_|Rest]) -> is_well_formed_list(Rest); is_well_formed_list(_) -> false. crasher(Node,Mod,Fun,Args,[],Reason) -> error_logger:warning_msg("** Can not start ~w:~w,~w on ~w **~n", [Mod,Fun,Args,Node]), exit(Reason); crasher(Node,Mod,Fun,Args,Opts,Reason) -> error_logger:warning_msg("** Can not start ~w:~w,~w (~w) on ~w **~n", [Mod,Fun,Args,Opts,Node]), exit(Reason). -spec erlang:yield() -> 'true'. yield() -> erlang:yield(). -spec nodes() -> Nodes when Nodes :: [node()]. nodes() -> erlang:nodes(visible). -spec disconnect_node(Node) -> boolean() | ignored when Node :: node(). disconnect_node(Node) -> net_kernel:disconnect(Node). -spec erlang:fun_info(Fun) -> [{Item, Info}] when Fun :: function(), Item :: arity | env | index | name | module | new_index | new_uniq | pid | type | uniq, Info :: term(). fun_info(Fun) when is_function(Fun) -> Keys = [type,env,arity,name,uniq,index,new_uniq,new_index,module,pid], fun_info_1(Keys, Fun, []). fun_info_1([K|Ks], Fun, A) -> case erlang:fun_info(Fun, K) of {K,undefined} -> fun_info_1(Ks, Fun, A); {K,_}=P -> fun_info_1(Ks, Fun, [P|A]) end; fun_info_1([], _, A) -> A. -type dst() :: pid() | port() | (RegName :: atom()) | {RegName :: atom(), Node :: node()}. -spec erlang:send_nosuspend(Dest, Msg) -> boolean() when Dest :: dst(), Msg :: term(). send_nosuspend(Pid, Msg) -> send_nosuspend(Pid, Msg, []). -spec erlang:send_nosuspend(Dest, Msg, Options) -> boolean() when Dest :: dst(), Msg :: term(), Options :: [noconnect]. send_nosuspend(Pid, Msg, Opts) -> case erlang:send(Pid, Msg, [nosuspend|Opts]) of ok -> true; _ -> false end. -spec erlang:localtime_to_universaltime({Date1, Time1}) -> {Date2, Time2} when Date1 :: calendar:date(), Date2 :: calendar:date(), Time1 :: calendar:time(), Time2 :: calendar:time(). localtime_to_universaltime(Localtime) -> erlang:localtime_to_universaltime(Localtime, undefined). -spec erlang:suspend_process(Suspendee) -> 'true' when Suspendee :: pid(). suspend_process(P) -> case catch erlang:suspend_process(P, []) of {'EXIT', {Reason, _}} -> erlang:error(Reason, [P]); {'EXIT', Reason} -> erlang:error(Reason, [P]); Res -> Res end. %% %% If the emulator wants to perform a distributed command and %% a connection is not established to the actual node the following %% functions are called in order to set up the connection and then %% reactivate the command. %% -spec dlink(pid() | port()) -> 'true'. dlink(Pid) -> case net_kernel:connect(node(Pid)) of true -> link(Pid); false -> erlang:dist_exit(self(), noconnection, Pid), true end. %% Can this ever happen? -spec dunlink(identifier()) -> 'true'. dunlink(Pid) -> case net_kernel:connect(node(Pid)) of true -> unlink(Pid); false -> true end. dmonitor_node(Node, Flag, []) -> case net_kernel:connect(Node) of true -> erlang:monitor_node(Node, Flag, []); false -> self() ! {nodedown, Node}, true end; dmonitor_node(Node, Flag, Opts) -> case lists:member(allow_passive_connect, Opts) of true -> case net_kernel:passive_cnct(Node) of true -> erlang:monitor_node(Node, Flag, Opts); false -> self() ! {nodedown, Node}, true end; _ -> dmonitor_node(Node,Flag,[]) end. dgroup_leader(Leader, Pid) -> case net_kernel:connect(node(Pid)) of true -> group_leader(Leader, Pid); false -> true %% bad arg ? end. dexit(Pid, Reason) -> case net_kernel:connect(node(Pid)) of true -> exit(Pid, Reason); false -> true end. dsend(Pid, Msg) when is_pid(Pid) -> case net_kernel:connect(node(Pid)) of true -> erlang:send(Pid, Msg); false -> Msg end; dsend(Port, Msg) when is_port(Port) -> case net_kernel:connect(node(Port)) of true -> erlang:send(Port, Msg); false -> Msg end; dsend({Name, Node}, Msg) -> case net_kernel:connect(Node) of true -> erlang:send({Name,Node}, Msg); false -> Msg; ignored -> Msg % Not distributed. end. dsend(Pid, Msg, Opts) when is_pid(Pid) -> case net_kernel:connect(node(Pid)) of true -> erlang:send(Pid, Msg, Opts); false -> ok end; dsend(Port, Msg, Opts) when is_port(Port) -> case net_kernel:connect(node(Port)) of true -> erlang:send(Port, Msg, Opts); false -> ok end; dsend({Name, Node}, Msg, Opts) -> case net_kernel:connect(Node) of true -> erlang:send({Name,Node}, Msg, Opts); false -> ok; ignored -> ok % Not distributed. end. -spec dmonitor_p('process', pid() | {atom(),atom()}) -> reference(). dmonitor_p(process, ProcSpec) -> %% ProcSpec = pid() | {atom(),atom()} %% ProcSpec CANNOT be an atom because a locally registered process %% is never handled here. Node = case ProcSpec of {S,N} when is_atom(S), is_atom(N), N =/= node() -> N; _ when is_pid(ProcSpec) -> node(ProcSpec) end, case net_kernel:connect(Node) of true -> erlang:monitor(process, ProcSpec); false -> Ref = make_ref(), self() ! {'DOWN', Ref, process, ProcSpec, noconnection}, Ref end. %% %% Trap function used when modified timing has been enabled. %% -spec delay_trap(Result, timeout()) -> Result. delay_trap(Result, 0) -> erlang:yield(), Result; delay_trap(Result, Timeout) -> receive after Timeout -> Result end. %% %% The business with different in and out cookies represented %% everywhere is discarded. %% A node has a cookie, connections/messages to that node use that cookie. %% Messages to us use our cookie. IF we change our cookie, other nodes %% have to reflect that, which we cannot forsee. %% -spec erlang:set_cookie(Node, Cookie) -> true when Node :: node(), Cookie :: atom(). set_cookie(Node, C) when Node =/= nonode@nohost, is_atom(Node) -> case is_atom(C) of true -> auth:set_cookie(Node, C); false -> error(badarg) end. -spec erlang:get_cookie() -> Cookie | nocookie when Cookie :: atom(). get_cookie() -> auth:get_cookie(). -spec concat_binary(ListOfBinaries) -> binary() when ListOfBinaries :: iolist(). concat_binary(List) -> list_to_binary(List). -spec integer_to_list(Integer, Base) -> string() when Integer :: integer(), Base :: 2..36. integer_to_list(I, 10) -> erlang:integer_to_list(I); integer_to_list(I, Base) when is_integer(I), is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 -> if I < 0 -> [$-|integer_to_list(-I, Base, [])]; true -> integer_to_list(I, Base, []) end; integer_to_list(I, Base) -> erlang:error(badarg, [I, Base]). integer_to_list(I0, Base, R0) -> D = I0 rem Base, I1 = I0 div Base, R1 = if D >= 10 -> [D-10+$A|R0]; true -> [D+$0|R0] end, if I1 =:= 0 -> R1; true -> integer_to_list(I1, Base, R1) end. -spec list_to_integer(String, Base) -> integer() when String :: string(), Base :: 2..36. list_to_integer(L, 10) -> erlang:list_to_integer(L); list_to_integer(L, Base) when is_list(L), is_integer(Base), Base >= 2, Base =< 1+$Z-$A+10 -> case list_to_integer_sign(L, Base) of I when is_integer(I) -> I; Fault -> erlang:error(Fault, [L,Base]) end; list_to_integer(L, Base) -> erlang:error(badarg, [L,Base]). list_to_integer_sign([$-|[_|_]=L], Base) -> case list_to_integer(L, Base, 0) of I when is_integer(I) -> -I; I -> I end; list_to_integer_sign([$+|[_|_]=L], Base) -> list_to_integer(L, Base, 0); list_to_integer_sign([_|_]=L, Base) -> list_to_integer(L, Base, 0); list_to_integer_sign(_, _) -> badarg. list_to_integer([D|L], Base, I) when is_integer(D), D >= $0, D =< $9, D < Base+$0 -> list_to_integer(L, Base, I*Base + D-$0); list_to_integer([D|L], Base, I) when is_integer(D), D >= $A, D < Base+$A-10 -> list_to_integer(L, Base, I*Base + D-$A+10); list_to_integer([D|L], Base, I) when is_integer(D), D >= $a, D < Base+$a-10 -> list_to_integer(L, Base, I*Base + D-$a+10); list_to_integer([], _, I) -> I; list_to_integer(_, _, _) -> badarg. %% erlang:flush_monitor_message/2 is for internal use only! %% %% erlang:demonitor(Ref, [flush]) traps to %% erlang:flush_monitor_message(Ref, Res) when %% it needs to flush a monitor message. flush_monitor_message(Ref, Res) when is_reference(Ref), is_atom(Res) -> receive {_, Ref, _, _, _} -> ok after 0 -> ok end, Res. -record(cpu, {node = -1, processor = -1, processor_node = -1, core = -1, thread = -1, logical = -1}). %% erlang:set_cpu_topology/1 is for internal use only! %% %% erlang:system_flag(cpu_topology, CpuTopology) traps to %% erlang:set_cpu_topology(CpuTopology). set_cpu_topology(CpuTopology) -> try format_cpu_topology(erlang:system_flag(internal_cpu_topology, cput_e2i(CpuTopology))) catch Class:Exception when Class =/= error; Exception =/= internal_error -> erlang:error(badarg, [CpuTopology]) end. cput_e2i_clvl({logical, _}, _PLvl) -> #cpu.logical; cput_e2i_clvl([E | _], PLvl) -> case element(1, E) of node -> case PLvl of 0 -> #cpu.node; #cpu.processor -> #cpu.processor_node end; processor -> case PLvl of 0 -> #cpu.node; #cpu.node -> #cpu.processor end; core -> #cpu.core; thread -> #cpu.thread end. cput_e2i(undefined) -> undefined; cput_e2i(E) -> rvrs(cput_e2i(E, -1, -1, #cpu{}, 0, cput_e2i_clvl(E, 0), [])). cput_e2i([], _NId, _PId, _IS, _PLvl, _Lvl, Res) -> Res; cput_e2i([E|Es], NId0, PId, IS, PLvl, Lvl, Res0) -> case cput_e2i(E, NId0, PId, IS, PLvl, Lvl, Res0) of [] -> cput_e2i(Es, NId0, PId, IS, PLvl, Lvl, Res0); [#cpu{node = N, processor = P, processor_node = PN} = CPU|_] = Res1 -> NId1 = case N > PN of true -> N; false -> PN end, cput_e2i(Es, NId1, P, CPU, PLvl, Lvl, Res1) end; cput_e2i({Tag, [], TagList}, Nid, PId, CPU, PLvl, Lvl, Res) -> %% Currently [] is the only valid InfoList cput_e2i({Tag, TagList}, Nid, PId, CPU, PLvl, Lvl, Res); cput_e2i({node, NL}, Nid0, PId, _CPU, 0, #cpu.node, Res) -> Nid1 = Nid0+1, Lvl = cput_e2i_clvl(NL, #cpu.node), cput_e2i(NL, Nid1, PId, #cpu{node = Nid1}, #cpu.node, Lvl, Res); cput_e2i({processor, PL}, Nid, PId0, _CPU, 0, #cpu.node, Res) -> PId1 = PId0+1, Lvl = cput_e2i_clvl(PL, #cpu.processor), cput_e2i(PL, Nid, PId1, #cpu{processor = PId1}, #cpu.processor, Lvl, Res); cput_e2i({processor, PL}, Nid, PId0, CPU, PLvl, CLvl, Res) when PLvl < #cpu.processor, CLvl =< #cpu.processor -> PId1 = PId0+1, Lvl = cput_e2i_clvl(PL, #cpu.processor), cput_e2i(PL, Nid, PId1, CPU#cpu{processor = PId1, processor_node = -1, core = -1, thread = -1}, #cpu.processor, Lvl, Res); cput_e2i({node, NL}, Nid0, PId, CPU, #cpu.processor, #cpu.processor_node, Res) -> Nid1 = Nid0+1, Lvl = cput_e2i_clvl(NL, #cpu.processor_node), cput_e2i(NL, Nid1, PId, CPU#cpu{processor_node = Nid1}, #cpu.processor_node, Lvl, Res); cput_e2i({core, CL}, Nid, PId, #cpu{core = C0} = CPU, PLvl, #cpu.core, Res) when PLvl < #cpu.core -> Lvl = cput_e2i_clvl(CL, #cpu.core), cput_e2i(CL, Nid, PId, CPU#cpu{core = C0+1, thread = -1}, #cpu.core, Lvl, Res); cput_e2i({thread, TL}, Nid, PId, #cpu{thread = T0} = CPU, PLvl, #cpu.thread, Res) when PLvl < #cpu.thread -> Lvl = cput_e2i_clvl(TL, #cpu.thread), cput_e2i(TL, Nid, PId, CPU#cpu{thread = T0+1}, #cpu.thread, Lvl, Res); cput_e2i({logical, ID}, _Nid, PId, #cpu{processor=P, core=C, thread=T} = CPU, PLvl, #cpu.logical, Res) when PLvl < #cpu.logical, is_integer(ID), 0 =< ID, ID < 65536 -> [CPU#cpu{processor = case P of -1 -> PId+1; _ -> P end, core = case C of -1 -> 0; _ -> C end, thread = case T of -1 -> 0; _ -> T end, logical = ID} | Res]. %% erlang:format_cpu_topology/1 is for internal use only! %% %% erlang:system_info(cpu_topology), %% and erlang:system_info({cpu_topology, Which}) traps to %% erlang:format_cpu_topology(InternalCpuTopology). format_cpu_topology(InternalCpuTopology) -> try cput_i2e(InternalCpuTopology) catch _ : _ -> erlang:error(internal_error, [InternalCpuTopology]) end. cput_i2e(undefined) -> undefined; cput_i2e(Is) -> cput_i2e(Is, true, #cpu.node, cput_i2e_tag_map()). cput_i2e([], _Frst, _Lvl, _TM) -> []; cput_i2e([#cpu{logical = LID}| _], _Frst, Lvl, _TM) when Lvl == #cpu.logical -> {logical, LID}; cput_i2e([#cpu{} = I | Is], Frst, Lvl, TM) -> cput_i2e(element(Lvl, I), Frst, Is, [I], Lvl, TM). cput_i2e(V, Frst, [I | Is], SameV, Lvl, TM) when V =:= element(Lvl, I) -> cput_i2e(V, Frst, Is, [I | SameV], Lvl, TM); cput_i2e(-1, true, [], SameV, Lvl, TM) -> cput_i2e(rvrs(SameV), true, Lvl+1, TM); cput_i2e(_V, true, [], SameV, Lvl, TM) when Lvl =/= #cpu.processor, Lvl =/= #cpu.processor_node -> cput_i2e(rvrs(SameV), true, Lvl+1, TM); cput_i2e(-1, _Frst, Is, SameV, #cpu.node, TM) -> cput_i2e(rvrs(SameV), true, #cpu.processor, TM) ++ cput_i2e(Is, false, #cpu.node, TM); cput_i2e(_V, _Frst, Is, SameV, Lvl, TM) -> [{cput_i2e_tag(Lvl, TM), cput_i2e(rvrs(SameV), true, Lvl+1, TM)} | cput_i2e(Is, false, Lvl, TM)]. cput_i2e_tag_map() -> list_to_tuple([cpu | record_info(fields, cpu)]). cput_i2e_tag(Lvl, TM) -> case element(Lvl, TM) of processor_node -> node; Other -> Other end. rvrs([_] = L) -> L; rvrs(Xs) -> rvrs(Xs, []). rvrs([],Ys) -> Ys; rvrs([X|Xs],Ys) -> rvrs(Xs, [X|Ys]). %% erlang:await_proc_exit/3 is for internal use only! %% %% BIFs that need to await a specific process exit before %% returning traps to erlang:await_proc_exit/3. %% %% NOTE: This function is tightly coupled to %% the implementation of the %% erts_bif_prep_await_proc_exit_*() %% functions in bif.c. Do not make %% any changes to it without reading %% the comment about them in bif.c! -spec await_proc_exit(dst(), 'apply' | 'data' | 'reason', term()) -> term(). await_proc_exit(Proc, Op, Data) -> Mon = erlang:monitor(process, Proc), receive {'DOWN', Mon, process, _Proc, Reason} -> case Op of apply -> {M, F, A} = Data, erlang:apply(M, F, A); data -> Data; reason -> Reason end end. -spec min(Term1, Term2) -> Minimum when Term1 :: term(), Term2 :: term(), Minimum :: term(). min(A, B) when A > B -> B; min(A, _) -> A. -spec max(Term1, Term2) -> Maximum when Term1 :: term(), Term2 :: term(), Maximum :: term(). max(A, B) when A < B -> B; max(A, _) -> A. %% %% erlang:memory/[0,1] %% %% NOTE! When updating these functions, make sure to also update %% erts_memory() in $ERL_TOP/erts/emulator/beam/erl_alloc.c %% -type memory_type() :: 'total' | 'processes' | 'processes_used' | 'system' | 'atom' | 'atom_used' | 'binary' | 'code' | 'ets' | 'low' | 'maximum'. -define(CARRIER_ALLOCS, [mseg_alloc, sbmbc_alloc, sbmbc_low_alloc]). -define(LOW_ALLOCS, [sbmbc_low_alloc, ll_low_alloc, std_low_alloc]). -define(ALL_NEEDED_ALLOCS, (erlang:system_info(alloc_util_allocators) -- ?CARRIER_ALLOCS)). -record(memory, {total = 0, processes = 0, processes_used = 0, system = 0, atom = 0, atom_used = 0, binary = 0, code = 0, ets = 0, low = 0, maximum = 0}). -spec memory() -> [{memory_type(), non_neg_integer()}]. memory() -> case aa_mem_data(au_mem_data(?ALL_NEEDED_ALLOCS)) of notsup -> erlang:error(notsup); Mem -> InstrTail = case Mem#memory.maximum of 0 -> []; _ -> [{maximum, Mem#memory.maximum}] end, Tail = case Mem#memory.low of 0 -> InstrTail; _ -> [{low, Mem#memory.low} | InstrTail] end, [{total, Mem#memory.total}, {processes, Mem#memory.processes}, {processes_used, Mem#memory.processes_used}, {system, Mem#memory.system}, {atom, Mem#memory.atom}, {atom_used, Mem#memory.atom_used}, {binary, Mem#memory.binary}, {code, Mem#memory.code}, {ets, Mem#memory.ets} | Tail] end. -spec memory(memory_type()|[memory_type()]) -> non_neg_integer() | [{memory_type(), non_neg_integer()}]. memory(Type) when is_atom(Type) -> {AA, ALCU, ChkSup, BadArgZero} = need_mem_info(Type), case get_mem_data(ChkSup, ALCU, AA) of notsup -> erlang:error(notsup, [Type]); Mem -> Value = get_memval(Type, Mem), case {BadArgZero, Value} of {true, 0} -> erlang:error(badarg, [Type]); _ -> Value end end; memory(Types) when is_list(Types) -> {AA, ALCU, ChkSup, BadArgZeroList} = need_mem_info_list(Types), case get_mem_data(ChkSup, ALCU, AA) of notsup -> erlang:error(notsup, [Types]); Mem -> case memory_result_list(Types, BadArgZeroList, Mem) of badarg -> erlang:error(badarg, [Types]); Result -> Result end end. memory_result_list([], [], _Mem) -> []; memory_result_list([T|Ts], [BAZ|BAZs], Mem) -> case memory_result_list(Ts, BAZs, Mem) of badarg -> badarg; TVs -> V = get_memval(T, Mem), case {BAZ, V} of {true, 0} -> badarg; _ -> [{T, V}| TVs] end end. get_mem_data(true, AlcUAllocs, NeedAllocatedAreas) -> case memory_is_supported() of false -> notsup; true -> get_mem_data(false, AlcUAllocs, NeedAllocatedAreas) end; get_mem_data(false, AlcUAllocs, NeedAllocatedAreas) -> AlcUMem = case AlcUAllocs of [] -> #memory{}; _ -> au_mem_data(AlcUAllocs) end, case NeedAllocatedAreas of true -> aa_mem_data(AlcUMem); false -> AlcUMem end. need_mem_info_list([]) -> {false, [], false, []}; need_mem_info_list([T|Ts]) -> {MAA, MALCU, MChkSup, MBadArgZero} = need_mem_info_list(Ts), {AA, ALCU, ChkSup, BadArgZero} = need_mem_info(T), {case AA of true -> true; _ -> MAA end, ALCU ++ (MALCU -- ALCU), case ChkSup of true -> true; _ -> MChkSup end, [BadArgZero|MBadArgZero]}. need_mem_info(Type) when Type == total; Type == system -> {true, ?ALL_NEEDED_ALLOCS, false, false}; need_mem_info(Type) when Type == processes; Type == processes_used -> {true, [eheap_alloc, fix_alloc], true, false}; need_mem_info(Type) when Type == atom; Type == atom_used; Type == code -> {true, [], true, false}; need_mem_info(binary) -> {false, [binary_alloc], true, false}; need_mem_info(ets) -> {true, [ets_alloc], true, false}; need_mem_info(low) -> LowAllocs = ?LOW_ALLOCS -- ?CARRIER_ALLOCS, {_, _, FeatureList, _} = erlang:system_info(allocator), AlcUAllocs = case LowAllocs -- FeatureList of [] -> LowAllocs; _ -> [] end, {false, AlcUAllocs, true, true}; need_mem_info(maximum) -> {true, [], true, true}; need_mem_info(_) -> {false, [], false, true}. get_memval(total, #memory{total = V}) -> V; get_memval(processes, #memory{processes = V}) -> V; get_memval(processes_used, #memory{processes_used = V}) -> V; get_memval(system, #memory{system = V}) -> V; get_memval(atom, #memory{atom = V}) -> V; get_memval(atom_used, #memory{atom_used = V}) -> V; get_memval(binary, #memory{binary = V}) -> V; get_memval(code, #memory{code = V}) -> V; get_memval(ets, #memory{ets = V}) -> V; get_memval(low, #memory{low = V}) -> V; get_memval(maximum, #memory{maximum = V}) -> V; get_memval(_, #memory{}) -> 0. memory_is_supported() -> {_, _, FeatureList, _} = erlang:system_info(allocator), case ((erlang:system_info(alloc_util_allocators) -- ?CARRIER_ALLOCS) -- FeatureList) of [] -> true; _ -> false end. get_blocks_size([{blocks_size, Sz, _, _} | Rest], Acc) -> get_blocks_size(Rest, Acc+Sz); get_blocks_size([{_, _, _, _} | Rest], Acc) -> get_blocks_size(Rest, Acc); get_blocks_size([], Acc) -> Acc. blocks_size([{Carriers, SizeList} | Rest], Acc) when Carriers == mbcs; Carriers == sbcs; Carriers == sbmbcs -> blocks_size(Rest, get_blocks_size(SizeList, Acc)); blocks_size([_ | Rest], Acc) -> blocks_size(Rest, Acc); blocks_size([], Acc) -> Acc. get_fix_proc([{ProcType, A1, U1}| Rest], {A0, U0}) when ProcType == proc; ProcType == monitor_sh; ProcType == nlink_sh; ProcType == msg_ref -> get_fix_proc(Rest, {A0+A1, U0+U1}); get_fix_proc([_|Rest], Acc) -> get_fix_proc(Rest, Acc); get_fix_proc([], Acc) -> Acc. fix_proc([{fix_types, SizeList} | _Rest], Acc) -> get_fix_proc(SizeList, Acc); fix_proc([_ | Rest], Acc) -> fix_proc(Rest, Acc); fix_proc([], Acc) -> Acc. is_low_alloc(_A, []) -> false; is_low_alloc(A, [A|_As]) -> true; is_low_alloc(A, [_A|As]) -> is_low_alloc(A, As). is_low_alloc(A) -> is_low_alloc(A, ?LOW_ALLOCS). au_mem_data(notsup, _) -> notsup; au_mem_data(_, [{_, false} | _]) -> notsup; au_mem_data(#memory{total = Tot, processes = Proc, processes_used = ProcU} = Mem, [{eheap_alloc, _, Data} | Rest]) -> Sz = blocks_size(Data, 0), au_mem_data(Mem#memory{total = Tot+Sz, processes = Proc+Sz, processes_used = ProcU+Sz}, Rest); au_mem_data(#memory{total = Tot, system = Sys, ets = Ets} = Mem, [{ets_alloc, _, Data} | Rest]) -> Sz = blocks_size(Data, 0), au_mem_data(Mem#memory{total = Tot+Sz, system = Sys+Sz, ets = Ets+Sz}, Rest); au_mem_data(#memory{total = Tot, system = Sys, binary = Bin} = Mem, [{binary_alloc, _, Data} | Rest]) -> Sz = blocks_size(Data, 0), au_mem_data(Mem#memory{total = Tot+Sz, system = Sys+Sz, binary = Bin+Sz}, Rest); au_mem_data(#memory{total = Tot, processes = Proc, processes_used = ProcU, system = Sys} = Mem, [{fix_alloc, _, Data} | Rest]) -> {A, U} = fix_proc(Data, {0, 0}), Sz = blocks_size(Data, 0), au_mem_data(Mem#memory{total = Tot+Sz, processes = Proc+A, processes_used = ProcU+U, system = Sys+Sz-A}, Rest); au_mem_data(#memory{total = Tot, system = Sys, low = Low} = Mem, [{A, _, Data} | Rest]) -> Sz = blocks_size(Data, 0), au_mem_data(Mem#memory{total = Tot+Sz, system = Sys+Sz, low = case is_low_alloc(A) of true -> Low+Sz; false -> Low end}, Rest); au_mem_data(EMD, []) -> EMD. au_mem_data(Allocs) -> Ref = make_ref(), erlang:system_info({allocator_sizes, Ref, Allocs}), receive_emd(Ref). receive_emd(_Ref, EMD, 0) -> EMD; receive_emd(Ref, EMD, N) -> receive {Ref, _, Data} -> receive_emd(Ref, au_mem_data(EMD, Data), N-1) end. receive_emd(Ref) -> receive_emd(Ref, #memory{}, erlang:system_info(schedulers)). aa_mem_data(notsup, _) -> notsup; aa_mem_data(#memory{} = Mem, [{maximum, Max} | Rest]) -> aa_mem_data(Mem#memory{maximum = Max}, Rest); aa_mem_data(#memory{} = Mem, [{total, Tot} | Rest]) -> aa_mem_data(Mem#memory{total = Tot, system = 0}, % system will be adjusted later Rest); aa_mem_data(#memory{atom = Atom, atom_used = AtomU} = Mem, [{atom_space, Alloced, Used} | Rest]) -> aa_mem_data(Mem#memory{atom = Atom+Alloced, atom_used = AtomU+Used}, Rest); aa_mem_data(#memory{atom = Atom, atom_used = AtomU} = Mem, [{atom_table, Sz} | Rest]) -> aa_mem_data(Mem#memory{atom = Atom+Sz, atom_used = AtomU+Sz}, Rest); aa_mem_data(#memory{ets = Ets} = Mem, [{ets_misc, Sz} | Rest]) -> aa_mem_data(Mem#memory{ets = Ets+Sz}, Rest); aa_mem_data(#memory{processes = Proc, processes_used = ProcU, system = Sys} = Mem, [{ProcData, Sz} | Rest]) when ProcData == bif_timer; ProcData == link_lh; ProcData == process_table -> aa_mem_data(Mem#memory{processes = Proc+Sz, processes_used = ProcU+Sz, system = Sys-Sz}, Rest); aa_mem_data(#memory{code = Code} = Mem, [{CodeData, Sz} | Rest]) when CodeData == module_table; CodeData == export_table; CodeData == export_list; CodeData == fun_table; CodeData == module_refs; CodeData == loaded_code -> aa_mem_data(Mem#memory{code = Code+Sz}, Rest); aa_mem_data(EMD, [{_, _} | Rest]) -> aa_mem_data(EMD, Rest); aa_mem_data(#memory{total = Tot, processes = Proc, system = Sys} = Mem, []) when Sys =< 0 -> %% Instrumented runtime system -> Sys = Tot - Proc Mem#memory{system = Tot - Proc}; aa_mem_data(EMD, []) -> EMD. aa_mem_data(notsup) -> notsup; aa_mem_data(EMD) -> aa_mem_data(EMD, erlang:system_info(allocated_areas)). %% %% alloc_info/1 and alloc_sizes/1 are for internal use only (used by %% erlang:system_info({allocator|allocator_sizes, _})). %% alloc_info(Allocs) -> get_alloc_info(allocator, Allocs). alloc_sizes(Allocs) -> get_alloc_info(allocator_sizes, Allocs). get_alloc_info(Type, AAtom) when is_atom(AAtom) -> [{AAtom, Result}] = get_alloc_info(Type, [AAtom]), Result; get_alloc_info(Type, AList) when is_list(AList) -> Ref = make_ref(), erlang:system_info({Type, Ref, AList}), receive_allocator(Ref, erlang:system_info(schedulers), mk_res_list(AList)). mk_res_list([]) -> []; mk_res_list([Alloc | Rest]) -> [{Alloc, []} | mk_res_list(Rest)]. insert_instance(I, N, []) -> [{instance, N, I}]; insert_instance(I, N, [{instance, M, _}|_] = Rest) when N < M -> [{instance, N, I} | Rest]; insert_instance(I, N, [Prev|Rest]) -> [Prev | insert_instance(I, N, Rest)]. insert_info([], Ys) -> Ys; insert_info([{A, false}|Xs], [{A, _IList}|Ys]) -> insert_info(Xs, [{A, false}|Ys]); insert_info([{A, N, I}|Xs], [{A, IList}|Ys]) -> insert_info(Xs, [{A, insert_instance(I, N, IList)}|Ys]); insert_info([{A1, _}|_] = Xs, [{A2, _} = Y | Ys]) when A1 /= A2 -> [Y | insert_info(Xs, Ys)]; insert_info([{A1, _, _}|_] = Xs, [{A2, _} = Y | Ys]) when A1 /= A2 -> [Y | insert_info(Xs, Ys)]. receive_allocator(_Ref, 0, Acc) -> Acc; receive_allocator(Ref, N, Acc) -> receive {Ref, _, InfoList} -> receive_allocator(Ref, N-1, insert_info(InfoList, Acc)) end.