%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2012. 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(cpu_sup).
%% API
-export([start_link/0, start/0, stop/0]).
-export([nprocs/0, avg1/0, avg5/0, avg15/0, util/0, util/1]).
-export([dummy_reply/1]).
%% For testing
-export([ping/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% Internal protocol with the port program
-define(nprocs,"n").
-define(avg1,"1").
-define(avg5,"5").
-define(avg15,"f").
-define(quit,"q").
-define(ping,"p").
-define(util,"u").
-define(cu_cpu_id, 0).
-define(cu_user, 1).
-define(cu_nice_user, 2).
-define(cu_kernel, 3).
-define(cu_io_wait, 4).
-define(cu_idle, 5).
-define(cu_hard_irq, 6).
-define(cu_soft_irq, 7).
-define(cu_steal, 8).
-define(INT32(D3,D2,D1,D0),
(((D3) bsl 24) bor ((D2) bsl 16) bor ((D1) bsl 8) bor (D0))).
-define(MAX_UINT32, ((1 bsl 32) - 1)).
-record(cpu_util, {cpu, busy = [], non_busy = []}).
-record(state, {server, os_type}).
%-record(state, {server, port = not_used, util = [], os_type}).
-record(internal, {port = not_used, util = [], os_type}).
%%----------------------------------------------------------------------
%% Contract specifications
%%----------------------------------------------------------------------
-type util_cpus() :: 'all' | integer() | [integer()].
-type util_state() :: 'user' | 'nice_user' | 'kernel' | 'wait' | 'idle'.
-type util_value() :: {util_state(), float()} | float().
-type util_desc() :: {util_cpus(), util_value(), util_value(), []}.
%%----------------------------------------------------------------------
%% Exported functions
%%----------------------------------------------------------------------
start() ->
gen_server:start({local, cpu_sup}, cpu_sup, [], []).
start_link() ->
gen_server:start_link({local, cpu_sup}, cpu_sup, [], []).
stop() ->
gen_server:call(cpu_sup, ?quit, infinity).
-spec nprocs() -> integer() | {'error', any()}.
nprocs() ->
os_mon:call(cpu_sup, ?nprocs, infinity).
-spec avg1() -> integer() | {'error', any()}.
avg1() ->
os_mon:call(cpu_sup, ?avg1, infinity).
-spec avg5() -> integer() | {'error', any()}.
avg5() ->
os_mon:call(cpu_sup, ?avg5, infinity).
-spec avg15() -> integer() | {'error', any()}.
avg15() ->
os_mon:call(cpu_sup, ?avg15, infinity).
-spec util(['detailed' | 'per_cpu']) ->
util_desc() | [util_desc()] | {'error', any()}.
util(Args) when is_list (Args) ->
% Get arguments
case lists:foldl(
fun (detailed, {_ , PC}) -> {true, PC };
(per_cpu , {D , _ }) -> {D , true};
(_ , _ ) -> badarg
end, {false, false}, Args) of
badarg ->
erlang:error(badarg);
{Detailed, PerCpu} ->
os_mon:call(cpu_sup, {?util, Detailed, PerCpu}, infinity)
end;
util(_) ->
erlang:error(badarg).
-spec util() -> float() | {'error', any()}.
util() ->
case util([]) of
{all, Busy, _, _} -> Busy;
Error -> Error
end.
dummy_reply(?nprocs) -> 0;
dummy_reply(?avg1) -> 0;
dummy_reply(?avg5) -> 0;
dummy_reply(?avg15) -> 0;
dummy_reply({?util,_,_}) -> {all, 0, 0, []}.
%%----------------------------------------------------------------------
%% For testing
%%----------------------------------------------------------------------
ping() ->
gen_server:call(cpu_sup,?ping).
%%----------------------------------------------------------------------
%% gen_server callbacks
%%----------------------------------------------------------------------
%% init
init([]) ->
process_flag(trap_exit, true),
process_flag(priority, low),
{ok,
#state{ os_type = os:type(),
server = measurement_server_start()
}
}.
handle_call(?quit, _From, State) ->
{stop, normal, ok, State};
handle_call({?util, D, PC}, {Client, _Tag},
#state{os_type = {unix, Flavor}} = State)
when Flavor == sunos;
Flavor == linux;
Flavor == freebsd ->
case measurement_server_call(State#state.server, {?util, D, PC, Client}) of
{error, Reason} ->
{ reply,
{error, Reason},
State#state{server=measurement_server_restart(State#state.server)}
};
Result -> {reply, Result, State}
end;
handle_call({?util, Detailed, PerCpu}, _From, State) ->
String = "OS_MON (cpu_sup), util/1 unavailable for this OS~n",
error_logger:warning_msg(String),
{reply, dummy_reply({?util, Detailed, PerCpu}), State};
handle_call(Request, _From, State) when Request==?nprocs;
Request==?avg1;
Request==?avg5;
Request==?avg15;
Request==?ping ->
case measurement_server_call(State#state.server, Request) of
{error, Reason} ->
{ reply,
{error, Reason},
State#state{server=measurement_server_restart(State#state.server)}
};
Result -> {reply, Result, State}
end.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({'EXIT', _Port, Reason}, State) ->
{stop, {server_died, Reason}, State#state{server=not_used}};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
exit(State#state.server, normal).
%% os_mon-2.0
%% For live downgrade to/upgrade from os_mon-1.8[.1]
code_change(Vsn, PrevState, "1.8") ->
case Vsn of
%% Downgrade from this version
{down, _Vsn} ->
process_flag(trap_exit, false);
%% Upgrade to this version
_Vsn ->
process_flag(trap_exit, true)
end,
{ok, PrevState};
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%----------------------------------------------------------------------
%% internal functions
%%----------------------------------------------------------------------
get_uint32_measurement(Request, #internal{os_type = {unix, linux}}) ->
{ok,F} = file:open("/proc/loadavg",[read,raw]),
{ok,D} = file:read_line(F),
ok = file:close(F),
{ok,[Load1,Load5,Load15,_PRun,PTotal],_} = io_lib:fread("~f ~f ~f ~d/~d", D),
case Request of
?avg1 -> sunify(Load1);
?avg5 -> sunify(Load5);
?avg15 -> sunify(Load15);
?ping -> 4711;
?nprocs -> PTotal
end;
get_uint32_measurement(Request, #internal{port = P, os_type = {unix, Sys}}) when
Sys == sunos;
Sys == dragonfly;
Sys == openbsd;
Sys == freebsd;
Sys == darwin ->
port_server_call(P, Request);
get_uint32_measurement(Request, #internal{os_type = {unix, Sys}}) when Sys == irix64;
Sys == irix ->
%% Get the load average using uptime.
%% "8:01pm up 2 days, 22:12, 4 users, load average: 0.70, 0.58, 0.43"
D = os:cmd("uptime") -- "\n",
Avg = lists:reverse(hd(string:tokens(lists:reverse(D), ":"))),
{ok, [L1, L5, L15], _} = io_lib:fread("~f, ~f, ~f", Avg),
case Request of
?avg1 -> sunify(L1);
?avg5 -> sunify(L5);
?avg15 -> sunify(L15);
?ping -> 4711;
?nprocs ->
{ok, ProcList} = file:list_dir("/proc/pinfo"),
length(ProcList)
end;
get_uint32_measurement(_, _) ->
throw(not_implemented).
get_util_measurement(?util, #internal{port = P }) ->
case port_server_call(P, ?util) of
{error, Error} -> {error, Error};
NewCpuUtil -> NewCpuUtil
end;
get_util_measurement(_,_) ->
throw(not_implemented).
%%----------------------------------------------------------------------
%% BEGIN: tainted internal functions
%%----------------------------------------------------------------------
sunify(Val) ->
round(Val*256). % Note that Solaris and Linux load averages are
% measured quite differently anyway
keysearchdelete(_, _, []) ->
{false, []};
keysearchdelete(K, N, [T|Ts]) when element(N, T) == K ->
{{value, T}, Ts};
keysearchdelete(K, N, [T|Ts]) ->
{X, NTs} = keysearchdelete(K, N, Ts),
{X, [T|NTs]}.
%% Internal cpu utilization functions
%% cpu_util_diff(New, Old) takes a list of new cpu_util records as first
%% argument and a list of old cpu_util records as second argument. The
%% two lists have to be sorted on cpu index in ascending order.
%%
%% The returned value is a difference list in descending order.
cpu_util_diff(New, Old) ->
cpu_util_diff(New, Old, []).
cpu_util_diff([], [], Acc) ->
Acc;
cpu_util_diff([#cpu_util{cpu = Cpu,
busy = NewBusy,
non_busy = NewNonBusy} | NewCpuUtils],
[#cpu_util{cpu = Cpu,
busy = OldBusy,
non_busy = OldNonBusy} | OldCpuUtils],
Acc) ->
{PreBusy, GotBusy} = state_list_diff(NewBusy, OldBusy),
{NonBusy, GotNonBusy} = state_list_diff(NewNonBusy, OldNonBusy),
Busy = case GotBusy orelse GotNonBusy of
true ->
PreBusy;
false ->
%% This can happen if cpu_sup:util/[0,1] is called
%% again immediately after the previous call has
%% returned. Because the user obviously is doing
%% something we charge "user".
lists:map(fun ({user, 0}) -> {user, 1};
({_, 0} = StateTup) -> StateTup
end,
PreBusy)
end,
cpu_util_diff(NewCpuUtils, OldCpuUtils, [#cpu_util{cpu = Cpu,
busy = Busy,
non_busy = NonBusy}
| Acc]);
%% A new cpu appeared
cpu_util_diff([#cpu_util{cpu = NC}|_] = New,
[#cpu_util{cpu = OC}|_] = Old,
Acc) when NC < OC ->
cpu_util_diff(New, [#cpu_util{cpu = NC}|Old], Acc);
cpu_util_diff([#cpu_util{cpu = NC}|_] = New, [], Acc) ->
cpu_util_diff(New, [#cpu_util{cpu = NC}], Acc);
%% An old cpu disappeared
cpu_util_diff([#cpu_util{cpu = NC}|Ns],
[#cpu_util{cpu = OC}|_] = Old,
Acc) when NC > OC ->
cpu_util_diff(Ns, Old, Acc);
cpu_util_diff([], _Old, Acc) ->
cpu_util_diff([], [], Acc).
cpu_util_rel(NewCpuUtils, OldCpuUtils, Detailed, PerCpu) ->
cpu_util_rel(cpu_util_diff(NewCpuUtils, OldCpuUtils), Detailed, PerCpu).
%%
%% cpu_util_rel/3 takes a difference list of cpu_util records as first
%% argument, a boolean determining if the result should be detailed as
%% second argument, and a boolean determining if the result should be
%% per cpu as third argument. The first argument (the difference list)
%% has to be sorted on cpu index in descending order.
%%
cpu_util_rel(CUDiff, false, false) ->
{B, T} = lists:foldl(fun (#cpu_util{busy = BusyList,
non_busy = NonBusyList},
{BusyAcc, TotAcc}) ->
Busy = state_list_sum(BusyList),
NonBusy = state_list_sum(NonBusyList),
{BusyAcc+Busy, TotAcc+Busy+NonBusy}
end,
{0, 0},
CUDiff),
BRel = B/T*100,
{all, BRel, 100-BRel, []};
cpu_util_rel(CUDiff, true, false) ->
cpu_util_rel_det(CUDiff, #cpu_util{cpu = [], busy = [], non_busy = []});
cpu_util_rel(CUDiff, false, true) ->
cpu_util_rel_pcpu(CUDiff, []);
cpu_util_rel(CUDiff, true, true) ->
cpu_util_rel_det_pcpu(CUDiff, []).
cpu_util_rel_pcpu([], Acc) ->
Acc;
cpu_util_rel_pcpu([#cpu_util{cpu = C,
busy = BusyList,
non_busy = NonBusyList} | Rest], Acc) ->
Busy = state_list_sum(BusyList),
NonBusy = state_list_sum(NonBusyList),
Tot = Busy + NonBusy,
cpu_util_rel_pcpu(Rest, [{C, Busy/Tot*100, NonBusy/Tot*100, []}|Acc]).
cpu_util_rel_det([], #cpu_util{cpu = CpuAcc,
busy = BusyAcc,
non_busy = NonBusyAcc}) ->
Total = state_list_sum(BusyAcc) + state_list_sum(NonBusyAcc),
{CpuAcc, mk_rel_states(BusyAcc,Total), mk_rel_states(NonBusyAcc,Total), []};
cpu_util_rel_det([#cpu_util{cpu = Cpu,
busy = Busy,
non_busy = NonBusy} | Rest],
#cpu_util{cpu = CpuAcc,
busy = BusyAcc,
non_busy = NonBusyAcc}) ->
cpu_util_rel_det(Rest, #cpu_util{cpu = [Cpu|CpuAcc],
busy = state_list_add(Busy,
BusyAcc),
non_busy = state_list_add(NonBusy,
NonBusyAcc)}).
cpu_util_rel_det_pcpu([], Acc) ->
Acc;
cpu_util_rel_det_pcpu([#cpu_util{cpu = Cpu,
busy = Busy,
non_busy = NonBusy}| Rest], Acc) ->
Total = state_list_sum(Busy) + state_list_sum(NonBusy),
cpu_util_rel_det_pcpu(Rest,
[{Cpu,
mk_rel_states(Busy, Total),
mk_rel_states(NonBusy, Total),
[]} | Acc]).
mk_rel_states(States, Total) ->
lists:map(fun ({State, Value}) -> {State, 100*Value/Total} end, States).
state_list_sum(StateList) ->
lists:foldl(fun ({_, X}, Acc) -> Acc+X end, 0, StateList).
state_list_diff([],[]) ->
{[], false};
state_list_diff([{State,ValueNew}|RestNew], []) ->
state_list_diff([{State, ValueNew} | RestNew], [{State, 0}]);
state_list_diff([{State,ValueNew}|RestNew], [{State,ValueOld}|RestOld]) ->
ValDiff = val_diff(State, ValueNew, ValueOld),
{RestStateDiff, FoundDiff} = state_list_diff(RestNew, RestOld),
{[{State, ValDiff} | RestStateDiff], FoundDiff orelse ValDiff /= 0}.
state_list_add([],[]) ->
[];
state_list_add([{State, ValueA}|RestA], []) ->
[{State, ValueA} | state_list_add(RestA, [])];
state_list_add([{State, ValueA} | RestA], [{State, ValueB} | RestB]) ->
[{State, ValueA + ValueB} | state_list_add(RestA, RestB)].
one_step_backwards(State, New, Old) ->
case os:type() of
{unix, linux} ->
%% This should never happen! But values sometimes takes a step
%% backwards on linux. We'll ignore it as long as it's only
%% one step...
0;
_ ->
val_diff2(State, New, Old)
end.
val_diff(State, New, Old) when New == Old - 1 ->
one_step_backwards(State, New, Old);
val_diff(State, ?MAX_UINT32, 0) ->
one_step_backwards(State, ?MAX_UINT32, 0);
val_diff(State, New, Old) ->
val_diff2(State, New, Old).
val_diff2(State, New, Old) when New > ?MAX_UINT32; Old > ?MAX_UINT32 ->
%% We obviously got uints > 32 bits
ensure_positive_diff(State, New - Old);
val_diff2(State, New, Old) when New < Old ->
%% 32-bit integer wrapped
ensure_positive_diff(State, (?MAX_UINT32 + 1) + New - Old);
val_diff2(_State, New, Old) ->
New - Old.
ensure_positive_diff(_State, Diff) when Diff >= 0 ->
Diff;
ensure_positive_diff(State, Diff) ->
throw({error, {negative_diff, State, Diff}}).
%%----------------------------------------------------------------------
%% END: tainted internal functions
%%----------------------------------------------------------------------
%%----------------------------------------------------------------------
%% cpu_sup measurement server wrapper
%%----------------------------------------------------------------------
measurement_server_call(Pid, Request) ->
Timeout = 5000,
Pid ! {self(), Request},
receive
{data, Data} -> Data
after Timeout ->
{error, timeout}
end.
measurement_server_restart(Pid) ->
exit(Pid, kill),
measurement_server_start().
measurement_server_start() ->
spawn(fun() -> measurement_server_init() end).
measurement_server_init() ->
process_flag(trap_exit, true),
OS = os:type(),
Server = case OS of
{unix, Flavor} when
Flavor==sunos;
Flavor==linux;
Flavor==darwin;
Flavor==freebsd;
Flavor==dragonfly;
Flavor==openbsd ->
{ok, Pid} = port_server_start_link(),
Pid;
{unix, Flavor} when
Flavor==irix64;
Flavor==irix ->
not_used;
_ ->
exit({unsupported_os, OS})
end,
measurement_server_loop(#internal{port=Server, os_type=OS}).
measurement_server_loop(State) ->
receive
{_, quit} ->
State#internal.port ! {self(), ?quit},
ok;
{'DOWN',Monitor,process,_,_} ->
measurement_server_loop(State#internal{ util = lists:keydelete(
Monitor,
2,
State#internal.util)});
{Pid, {?util, D, PC, Client}} ->
{Monitor, OldCpuUtil, Utils2} = case keysearchdelete(Client, 1, State#internal.util) of
{{value, {Client, Mon, U}}, Us} -> {Mon, U, Us};
{false, Us} -> {erlang:monitor(process, Client), [], Us}
end,
try get_util_measurement(?util, State) of
NewCpuUtil ->
Result = cpu_util_rel(NewCpuUtil, OldCpuUtil, D, PC),
Pid ! {data, Result},
measurement_server_loop(State#internal{util=[{Client,Monitor,NewCpuUtil}|Utils2]})
catch
Error ->
Pid ! {error, Error},
measurement_server_loop(State)
end;
{Pid, Request} ->
try get_uint32_measurement(Request, State) of
Result -> Pid ! {data, Result}
catch
Error -> Pid ! {error, Error}
end,
measurement_server_loop(State);
{'EXIT', OldPid, _n} when State#internal.port == OldPid ->
{ok, NewPid} = port_server_start_link(),
measurement_server_loop(State#internal{port = NewPid});
_Other ->
measurement_server_loop(State)
end.
%%----------------------------------------------------------------------
%% cpu_sup port program server wrapper
%%----------------------------------------------------------------------
port_server_call(Pid, Command) ->
Pid ! {self(), Command},
receive
{Pid, {data, Result}} -> Result;
{Pid, {error, Reason}} -> {error, Reason}
end.
port_server_start_link() ->
Timeout = 6000,
Pid = spawn_link(fun() -> port_server_init(Timeout) end),
Pid ! {self(), ?ping},
receive
{Pid, {data,4711}} -> {ok, Pid};
{error,Reason} -> {error, Reason}
after Timeout ->
{error, timeout}
end.
port_server_init(Timeout) ->
Port = start_portprogram(),
port_server_loop(Port, Timeout).
port_server_loop(Port, Timeout) ->
receive
% Adjust timeout
{Pid, {timeout, Timeout}} ->
Pid ! {data, Timeout},
port_server_loop(Port, Timeout);
% Number of processors
{Pid, ?nprocs} ->
port_command(Port, ?nprocs),
Result = port_receive_uint32(Port, Timeout),
Pid ! {self(), {data, Result}},
port_server_loop(Port, Timeout);
% Average load for the past minute
{Pid, ?avg1} ->
port_command(Port, ?avg1),
Result = port_receive_uint32(Port, Timeout),
Pid ! {self(), {data, Result}},
port_server_loop(Port, Timeout);
% Average load for the past five minutes
{Pid, ?avg5} ->
port_command(Port, ?avg5),
Result = port_receive_uint32(Port, Timeout),
Pid ! {self(), {data, Result}},
port_server_loop(Port, Timeout);
% Average load for the past 15 minutes
{Pid, ?avg15} ->
port_command(Port, ?avg15),
Result = port_receive_uint32(Port, Timeout),
Pid ! {self(), {data, Result}},
port_server_loop(Port, Timeout);
{Pid, ?util} ->
port_command(Port, ?util),
Result = port_receive_util(Port, Timeout),
Pid ! {self(), {data, Result}},
port_server_loop(Port, Timeout);
% Port ping
{Pid, ?ping} ->
port_command(Port, ?ping),
Result = port_receive_uint32(Port, Timeout),
Pid ! {self(), {data, Result}},
port_server_loop(Port, Timeout);
% Close port and this server
{Pid, ?quit} ->
port_command(Port, ?quit),
port_close(Port),
Pid ! {self(), {data, quit}},
ok;
% Ignore other commands
_ -> port_server_loop(Port, Timeout)
end.
port_receive_uint32( Port, Timeout) -> port_receive_uint32(Port, Timeout, []).
port_receive_uint32(_Port, _Timeout, [D3,D2,D1,D0]) -> ?INT32(D3,D2,D1,D0);
port_receive_uint32(_Port, _Timeout, [_,_,_,_ | G]) -> exit({port_garbage, G});
port_receive_uint32(Port, Timeout, D) ->
receive
{'EXIT', Port, Reason} -> exit({port_exit, Reason});
{Port, {data, ND}} -> port_receive_uint32(Port, Timeout, D ++ ND)
after Timeout -> exit(timeout_uint32) end.
port_receive_util(Port, Timeout) ->
receive
{Port, {data, [ NP3,NP2,NP1,NP0, % Number of processors
NE3,NE2,NE1,NE0 % Number of entries per processor
| CpuData]}} ->
port_receive_cpu_util( ?INT32(NP3,NP2,NP1,NP0),
?INT32(NE3,NE2,NE1,NE0),
CpuData, []);
{'EXIT', Port, Reason} -> exit({port_exit, Reason})
after Timeout -> exit(timeout_util) end.
% per processor receive loop
port_receive_cpu_util(0, _NE, [], CpuList) ->
% Return in ascending cpu_id order
lists:reverse(CpuList);
port_receive_cpu_util(0, _NE, Garbage, _) ->
exit( {port_garbage, Garbage});
port_receive_cpu_util(NP, NE, CpuData, CpuList) ->
{CpuUtil, Rest} = port_receive_cpu_util_entries(NE, #cpu_util{}, CpuData),
port_receive_cpu_util(NP - 1, NE, Rest, [ CpuUtil | CpuList]).
% per entry receive loop
port_receive_cpu_util_entries(0, CU, Rest) ->
{CU, Rest};
port_receive_cpu_util_entries(NE, CU,
[ CID3, CID2, CID1, CID0,
Val3, Val2, Val1, Val0 |
CpuData]) ->
TagId = ?INT32(CID3,CID2,CID1,CID0),
Value = ?INT32(Val3,Val2,Val1,Val0),
% Conversions from integers to atoms
case TagId of
?cu_cpu_id ->
NewCU = CU#cpu_util{cpu = Value},
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
?cu_user ->
NewCU = CU#cpu_util{
busy = [{user, Value} | CU#cpu_util.busy] },
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
?cu_nice_user ->
NewCU = CU#cpu_util{
busy = [{nice_user, Value} | CU#cpu_util.busy] },
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
?cu_kernel ->
NewCU = CU#cpu_util{
busy = [{kernel, Value} | CU#cpu_util.busy] },
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
?cu_io_wait ->
NewCU = CU#cpu_util{
non_busy = [{wait, Value} | CU#cpu_util.non_busy] },
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
?cu_idle ->
NewCU = CU#cpu_util{
non_busy = [{idle, Value} | CU#cpu_util.non_busy] },
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
?cu_hard_irq ->
NewCU = CU#cpu_util{
busy = [{hard_irq, Value} | CU#cpu_util.busy] },
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
?cu_soft_irq ->
NewCU = CU#cpu_util{
busy = [{soft_irq, Value} | CU#cpu_util.busy] },
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
?cu_steal ->
NewCU = CU#cpu_util{
non_busy = [{steal, Value} | CU#cpu_util.non_busy] },
port_receive_cpu_util_entries(NE - 1, NewCU, CpuData);
Unhandled ->
exit({unexpected_type_id, Unhandled})
end;
port_receive_cpu_util_entries(_, _, Data) ->
exit({data_mismatch, Data}).
start_portprogram() ->
Port = os_mon:open_port("cpu_sup", [stream]),
port_command(Port, ?ping),
4711 = port_receive_uint32(Port, 5000),
Port.