1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(cpu_sup). 21 22%% API 23-export([start_link/0, start/0, stop/0]). 24-export([nprocs/0, avg1/0, avg5/0, avg15/0, util/0, util/1]). 25-export([dummy_reply/1]). 26 27%% For testing 28-export([ping/0]). 29 30%% gen_server callbacks 31-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 32 terminate/2, code_change/3]). 33 34%% Internal protocol with the port program 35-define(nprocs,"n"). 36-define(avg1,"1"). 37-define(avg5,"5"). 38-define(avg15,"f"). 39-define(quit,"q"). 40-define(ping,"p"). 41-define(util,"u"). 42 43-define(cu_cpu_id, 0). 44-define(cu_user, 1). 45-define(cu_nice_user, 2). 46-define(cu_kernel, 3). 47-define(cu_io_wait, 4). 48-define(cu_idle, 5). 49-define(cu_hard_irq, 6). 50-define(cu_soft_irq, 7). 51-define(cu_steal, 8). 52 53-define(INT32(D3,D2,D1,D0), 54 (((D3) bsl 24) bor ((D2) bsl 16) bor ((D1) bsl 8) bor (D0))). 55 56-define(MAX_UINT32, ((1 bsl 32) - 1)). 57 58-record(cpu_util, {cpu, busy = [], non_busy = []}). 59 60-record(state, {server, os_type}). 61%-record(state, {server, port = not_used, util = [], os_type}). 62 63-record(internal, {port = not_used, util = [], os_type}). 64 65%%---------------------------------------------------------------------- 66%% Contract specifications 67%%---------------------------------------------------------------------- 68 69-type util_cpus() :: 'all' | integer() | [integer()]. 70-type util_state() :: 'user' | 'nice_user' | 'kernel' | 'wait' | 'idle'. 71-type util_value() :: [{util_state(), float()}] | float(). 72-type util_desc() :: {util_cpus(), util_value(), util_value(), []}. 73 74%%---------------------------------------------------------------------- 75%% Exported functions 76%%---------------------------------------------------------------------- 77 78start() -> 79 gen_server:start({local, cpu_sup}, cpu_sup, [], []). 80 81start_link() -> 82 gen_server:start_link({local, cpu_sup}, cpu_sup, [], []). 83 84stop() -> 85 gen_server:call(cpu_sup, ?quit, infinity). 86 87-spec nprocs() -> integer() | {'error', any()}. 88 89nprocs() -> 90 os_mon:call(cpu_sup, ?nprocs, infinity). 91 92-spec avg1() -> integer() | {'error', any()}. 93 94avg1() -> 95 os_mon:call(cpu_sup, ?avg1, infinity). 96 97-spec avg5() -> integer() | {'error', any()}. 98 99avg5() -> 100 os_mon:call(cpu_sup, ?avg5, infinity). 101 102-spec avg15() -> integer() | {'error', any()}. 103 104avg15() -> 105 os_mon:call(cpu_sup, ?avg15, infinity). 106 107-spec util(['detailed' | 'per_cpu']) -> 108 util_desc() | [util_desc()] | {'error', any()}. 109 110util(Args) when is_list (Args) -> 111 % Get arguments 112 case lists:foldl( 113 fun (detailed, {_ , PC}) -> {true, PC }; 114 (per_cpu , {D , _ }) -> {D , true}; 115 (_ , _ ) -> badarg 116 end, {false, false}, Args) of 117 badarg -> 118 erlang:error(badarg); 119 {Detailed, PerCpu} -> 120 os_mon:call(cpu_sup, {?util, Detailed, PerCpu}, infinity) 121 end; 122util(_) -> 123 erlang:error(badarg). 124 125-spec util() -> float() | {'error', any()}. 126 127util() -> 128 case util([]) of 129 {all, Busy, _, _} -> Busy; 130 Error -> Error 131 end. 132 133dummy_reply(?nprocs) -> 0; 134dummy_reply(?avg1) -> 0; 135dummy_reply(?avg5) -> 0; 136dummy_reply(?avg15) -> 0; 137dummy_reply({?util,_,_}) -> {all, 0, 0, []}. 138 139%%---------------------------------------------------------------------- 140%% For testing 141%%---------------------------------------------------------------------- 142 143ping() -> 144 gen_server:call(cpu_sup,?ping). 145 146%%---------------------------------------------------------------------- 147%% gen_server callbacks 148%%---------------------------------------------------------------------- 149 150%% init 151init([]) -> 152 process_flag(trap_exit, true), 153 process_flag(priority, low), 154 {ok, 155 #state{ os_type = os:type(), 156 server = measurement_server_start() 157 } 158 }. 159handle_call(?quit, _From, State) -> 160 {stop, normal, ok, State}; 161handle_call({?util, D, PC}, {Client, _Tag}, 162 #state{os_type = {unix, Flavor}} = State) 163 when Flavor == sunos; 164 Flavor == linux; 165 Flavor == freebsd; 166 Flavor == darwin -> 167 case measurement_server_call(State#state.server, {?util, D, PC, Client}) of 168 {error, Reason} -> 169 { reply, 170 {error, Reason}, 171 State#state{server=measurement_server_restart(State#state.server)} 172 }; 173 Result -> {reply, Result, State} 174 end; 175handle_call({?util, Detailed, PerCpu}, _From, State) -> 176 String = "OS_MON (cpu_sup), util/1 unavailable for this OS~n", 177 error_logger:warning_msg(String), 178 {reply, dummy_reply({?util, Detailed, PerCpu}), State}; 179handle_call(Request, _From, State) when Request==?nprocs; 180 Request==?avg1; 181 Request==?avg5; 182 Request==?avg15; 183 Request==?ping -> 184 case measurement_server_call(State#state.server, Request) of 185 {error, Reason} -> 186 { reply, 187 {error, Reason}, 188 State#state{server=measurement_server_restart(State#state.server)} 189 }; 190 Result -> {reply, Result, State} 191 end. 192handle_cast(_Msg, State) -> 193 {noreply, State}. 194handle_info({'EXIT', _Port, Reason}, State) -> 195 {stop, {server_died, Reason}, State#state{server=not_used}}; 196handle_info(_Info, State) -> 197 {noreply, State}. 198 199terminate(_Reason, State) -> 200 exit(State#state.server, normal). 201 202%% os_mon-2.0 203%% For live downgrade to/upgrade from os_mon-1.8[.1] 204code_change(Vsn, PrevState, "1.8") -> 205 case Vsn of 206 207 %% Downgrade from this version 208 {down, _Vsn} -> 209 process_flag(trap_exit, false); 210 211 %% Upgrade to this version 212 _Vsn -> 213 process_flag(trap_exit, true) 214 end, 215 {ok, PrevState}; 216code_change(_OldVsn, State, _Extra) -> 217 {ok, State}. 218 219%%---------------------------------------------------------------------- 220%% internal functions 221%%---------------------------------------------------------------------- 222 223get_uint32_measurement(Request, #internal{port = P, os_type = {unix, linux}}) -> 224 case file:open("/proc/loadavg",[read,raw]) of 225 {ok,F} -> 226 {ok,D} = file:read_line(F), 227 ok = file:close(F), 228 {ok,[Load1,Load5,Load15,_PRun,PTotal],_} = io_lib:fread("~f ~f ~f ~d/~d", D), 229 case Request of 230 ?avg1 -> sunify(Load1); 231 ?avg5 -> sunify(Load5); 232 ?avg15 -> sunify(Load15); 233 ?ping -> 4711; 234 ?nprocs -> PTotal 235 end; 236 {error,_} -> 237 port_server_call(P, Request) 238 end; 239get_uint32_measurement(Request, #internal{port = P, os_type = {unix, Sys}}) when 240 Sys == sunos; 241 Sys == dragonfly; 242 Sys == openbsd; 243 Sys == freebsd; 244 Sys == darwin -> 245 port_server_call(P, Request); 246get_uint32_measurement(Request, #internal{os_type = {unix, Sys}}) when Sys == irix64; 247 Sys == irix -> 248 %% Get the load average using uptime. 249 %% "8:01pm up 2 days, 22:12, 4 users, load average: 0.70, 0.58, 0.43" 250 D = os:cmd("uptime") -- "\n", 251 Avg = lists:reverse(hd(string:lexemes(lists:reverse(D), ":"))), 252 {ok, [L1, L5, L15], _} = io_lib:fread("~f, ~f, ~f", Avg), 253 case Request of 254 ?avg1 -> sunify(L1); 255 ?avg5 -> sunify(L5); 256 ?avg15 -> sunify(L15); 257 ?ping -> 4711; 258 ?nprocs -> 259 {ok, ProcList} = file:list_dir("/proc/pinfo"), 260 length(ProcList) 261 end; 262get_uint32_measurement(_, _) -> 263 throw(not_implemented). 264 265 266get_util_measurement(?util, #internal{port = P }) -> 267 case port_server_call(P, ?util) of 268 {error, Error} -> {error, Error}; 269 NewCpuUtil -> NewCpuUtil 270 end; 271get_util_measurement(_,_) -> 272 throw(not_implemented). 273 274%%---------------------------------------------------------------------- 275%% BEGIN: tainted internal functions 276%%---------------------------------------------------------------------- 277 278sunify(Val) -> 279 round(Val*256). % Note that Solaris and Linux load averages are 280 % measured quite differently anyway 281 282 283keysearchdelete(_, _, []) -> 284 {false, []}; 285keysearchdelete(K, N, [T|Ts]) when element(N, T) == K -> 286 {{value, T}, Ts}; 287keysearchdelete(K, N, [T|Ts]) -> 288 {X, NTs} = keysearchdelete(K, N, Ts), 289 {X, [T|NTs]}. 290 291%% Internal cpu utilization functions 292 293%% cpu_util_diff(New, Old) takes a list of new cpu_util records as first 294%% argument and a list of old cpu_util records as second argument. The 295%% two lists have to be sorted on cpu index in ascending order. 296%% 297%% The returned value is a difference list in descending order. 298cpu_util_diff(New, Old) -> 299 cpu_util_diff(New, Old, []). 300 301cpu_util_diff([], [], Acc) -> 302 Acc; 303cpu_util_diff([#cpu_util{cpu = Cpu, 304 busy = NewBusy, 305 non_busy = NewNonBusy} | NewCpuUtils], 306 [#cpu_util{cpu = Cpu, 307 busy = OldBusy, 308 non_busy = OldNonBusy} | OldCpuUtils], 309 Acc) -> 310 {PreBusy, GotBusy} = state_list_diff(NewBusy, OldBusy), 311 {NonBusy, GotNonBusy} = state_list_diff(NewNonBusy, OldNonBusy), 312 Busy = case GotBusy orelse GotNonBusy of 313 true -> 314 PreBusy; 315 false -> 316 %% This can happen if cpu_sup:util/[0,1] is called 317 %% again immediately after the previous call has 318 %% returned. Because the user obviously is doing 319 %% something we charge "user". 320 lists:map(fun ({user, 0}) -> {user, 1}; 321 ({_, 0} = StateTup) -> StateTup 322 end, 323 PreBusy) 324 end, 325cpu_util_diff(NewCpuUtils, OldCpuUtils, [#cpu_util{cpu = Cpu, 326 busy = Busy, 327 non_busy = NonBusy} 328 | Acc]); 329 330%% A new cpu appeared 331cpu_util_diff([#cpu_util{cpu = NC}|_] = New, 332 [#cpu_util{cpu = OC}|_] = Old, 333 Acc) when NC < OC -> 334cpu_util_diff(New, [#cpu_util{cpu = NC}|Old], Acc); 335cpu_util_diff([#cpu_util{cpu = NC}|_] = New, [], Acc) -> 336cpu_util_diff(New, [#cpu_util{cpu = NC}], Acc); 337 338%% An old cpu disappeared 339cpu_util_diff([#cpu_util{cpu = NC}|Ns], 340 [#cpu_util{cpu = OC}|_] = Old, 341 Acc) when NC > OC -> 342cpu_util_diff(Ns, Old, Acc); 343cpu_util_diff([], _Old, Acc) -> 344cpu_util_diff([], [], Acc). 345 346cpu_util_rel(NewCpuUtils, OldCpuUtils, Detailed, PerCpu) -> 347 cpu_util_rel(cpu_util_diff(NewCpuUtils, OldCpuUtils), Detailed, PerCpu). 348 349%% 350%% cpu_util_rel/3 takes a difference list of cpu_util records as first 351%% argument, a boolean determining if the result should be detailed as 352%% second argument, and a boolean determining if the result should be 353%% per cpu as third argument. The first argument (the difference list) 354%% has to be sorted on cpu index in descending order. 355%% 356cpu_util_rel(CUDiff, false, false) -> 357 {B, T} = lists:foldl(fun (#cpu_util{busy = BusyList, 358 non_busy = NonBusyList}, 359 {BusyAcc, TotAcc}) -> 360 Busy = state_list_sum(BusyList), 361 NonBusy = state_list_sum(NonBusyList), 362 {BusyAcc+Busy, TotAcc+Busy+NonBusy} 363 end, 364 {0, 0}, 365 CUDiff), 366 BRel = B/T*100, 367 {all, BRel, 100-BRel, []}; 368cpu_util_rel(CUDiff, true, false) -> 369 cpu_util_rel_det(CUDiff, #cpu_util{cpu = [], busy = [], non_busy = []}); 370cpu_util_rel(CUDiff, false, true) -> 371 cpu_util_rel_pcpu(CUDiff, []); 372cpu_util_rel(CUDiff, true, true) -> 373 cpu_util_rel_det_pcpu(CUDiff, []). 374 375cpu_util_rel_pcpu([], Acc) -> 376 Acc; 377cpu_util_rel_pcpu([#cpu_util{cpu = C, 378 busy = BusyList, 379 non_busy = NonBusyList} | Rest], Acc) -> 380 Busy = state_list_sum(BusyList), 381 NonBusy = state_list_sum(NonBusyList), 382 Tot = Busy + NonBusy, 383 cpu_util_rel_pcpu(Rest, [{C, Busy/Tot*100, NonBusy/Tot*100, []}|Acc]). 384 385cpu_util_rel_det([], #cpu_util{cpu = CpuAcc, 386 busy = BusyAcc, 387 non_busy = NonBusyAcc}) -> 388 Total = state_list_sum(BusyAcc) + state_list_sum(NonBusyAcc), 389 {CpuAcc, mk_rel_states(BusyAcc,Total), mk_rel_states(NonBusyAcc,Total), []}; 390cpu_util_rel_det([#cpu_util{cpu = Cpu, 391 busy = Busy, 392 non_busy = NonBusy} | Rest], 393 #cpu_util{cpu = CpuAcc, 394 busy = BusyAcc, 395 non_busy = NonBusyAcc}) -> 396 cpu_util_rel_det(Rest, #cpu_util{cpu = [Cpu|CpuAcc], 397 busy = state_list_add(Busy, 398 BusyAcc), 399 non_busy = state_list_add(NonBusy, 400 NonBusyAcc)}). 401 402cpu_util_rel_det_pcpu([], Acc) -> 403 Acc; 404cpu_util_rel_det_pcpu([#cpu_util{cpu = Cpu, 405 busy = Busy, 406 non_busy = NonBusy}| Rest], Acc) -> 407 Total = state_list_sum(Busy) + state_list_sum(NonBusy), 408 cpu_util_rel_det_pcpu(Rest, 409 [{Cpu, 410 mk_rel_states(Busy, Total), 411 mk_rel_states(NonBusy, Total), 412 []} | Acc]). 413 414mk_rel_states(States, Total) -> 415 lists:map(fun ({State, Value}) -> {State, 100*Value/Total} end, States). 416 417state_list_sum(StateList) -> 418 lists:foldl(fun ({_, X}, Acc) -> Acc+X end, 0, StateList). 419 420state_list_diff([],[]) -> 421 {[], false}; 422state_list_diff([{State,ValueNew}|RestNew], []) -> 423 state_list_diff([{State, ValueNew} | RestNew], [{State, 0}]); 424state_list_diff([{State,ValueNew}|RestNew], [{State,ValueOld}|RestOld]) -> 425 ValDiff = val_diff(State, ValueNew, ValueOld), 426 {RestStateDiff, FoundDiff} = state_list_diff(RestNew, RestOld), 427 {[{State, ValDiff} | RestStateDiff], FoundDiff orelse ValDiff /= 0}. 428 429state_list_add([],[]) -> 430 []; 431state_list_add([{State, ValueA}|RestA], []) -> 432 [{State, ValueA} | state_list_add(RestA, [])]; 433state_list_add([{State, ValueA} | RestA], [{State, ValueB} | RestB]) -> 434 [{State, ValueA + ValueB} | state_list_add(RestA, RestB)]. 435 436one_step_backwards(State, New, Old) -> 437 case os:type() of 438 {unix, linux} -> 439 %% This should never happen! But values sometimes takes a step 440 %% backwards on linux. We'll ignore it as long as it's only 441 %% one step... 442 0; 443 _ -> 444 val_diff2(State, New, Old) 445 end. 446 447val_diff(State, New, Old) when New == Old - 1 -> 448 one_step_backwards(State, New, Old); 449val_diff(State, ?MAX_UINT32, 0) -> 450 one_step_backwards(State, ?MAX_UINT32, 0); 451val_diff(State, New, Old) -> 452 val_diff2(State, New, Old). 453 454val_diff2(State, New, Old) when New > ?MAX_UINT32; Old > ?MAX_UINT32 -> 455 %% We obviously got uints > 32 bits 456 ensure_positive_diff(State, New - Old); 457val_diff2(State, New, Old) when New < Old -> 458 %% 32-bit integer wrapped 459 ensure_positive_diff(State, (?MAX_UINT32 + 1) + New - Old); 460val_diff2(_State, New, Old) -> 461 New - Old. 462 463ensure_positive_diff(_State, Diff) when Diff >= 0 -> 464 Diff; 465ensure_positive_diff(State, Diff) -> 466 throw({error, {negative_diff, State, Diff}}). 467%%---------------------------------------------------------------------- 468%% END: tainted internal functions 469%%---------------------------------------------------------------------- 470 471%%---------------------------------------------------------------------- 472%% cpu_sup measurement server wrapper 473%%---------------------------------------------------------------------- 474 475measurement_server_call(Pid, Request) -> 476 Timeout = 5000, 477 Pid ! {self(), Request}, 478 receive 479 {data, Data} -> Data 480 after Timeout -> 481 {error, timeout} 482 end. 483 484measurement_server_restart(Pid) -> 485 exit(Pid, kill), 486 measurement_server_start(). 487 488measurement_server_start() -> 489 spawn(fun() -> measurement_server_init() end). 490 491measurement_server_init() -> 492 process_flag(trap_exit, true), 493 OS = os:type(), 494 Server = case OS of 495 {unix, Flavor} when 496 Flavor==sunos; 497 Flavor==linux; 498 Flavor==darwin; 499 Flavor==freebsd; 500 Flavor==dragonfly; 501 Flavor==openbsd -> 502 {ok, Pid} = port_server_start_link(), 503 Pid; 504 {unix, Flavor} when 505 Flavor==irix64; 506 Flavor==irix -> 507 not_used; 508 _ -> 509 exit({unsupported_os, OS}) 510 end, 511 measurement_server_loop(#internal{port=Server, os_type=OS}). 512 513measurement_server_loop(State) -> 514 receive 515 {_, quit} -> 516 State#internal.port ! {self(), ?quit}, 517 ok; 518 {'DOWN',Monitor,process,_,_} -> 519 measurement_server_loop(State#internal{ util = lists:keydelete( 520 Monitor, 521 2, 522 State#internal.util)}); 523 {Pid, {?util, D, PC, Client}} -> 524 {Monitor, OldCpuUtil, Utils2} = case keysearchdelete(Client, 1, State#internal.util) of 525 {{value, {Client, Mon, U}}, Us} -> {Mon, U, Us}; 526 {false, Us} -> {erlang:monitor(process, Client), [], Us} 527 end, 528 try get_util_measurement(?util, State) of 529 NewCpuUtil -> 530 Result = cpu_util_rel(NewCpuUtil, OldCpuUtil, D, PC), 531 Pid ! {data, Result}, 532 measurement_server_loop(State#internal{util=[{Client,Monitor,NewCpuUtil}|Utils2]}) 533 catch 534 Error -> 535 Pid ! {error, Error}, 536 measurement_server_loop(State) 537 end; 538 {Pid, Request} -> 539 _ = try get_uint32_measurement(Request, State) of 540 Result -> Pid ! {data, Result} 541 catch 542 Error -> Pid ! {error, Error} 543 end, 544 measurement_server_loop(State); 545 {'EXIT', OldPid, _n} when State#internal.port == OldPid -> 546 {ok, NewPid} = port_server_start_link(), 547 measurement_server_loop(State#internal{port = NewPid}); 548 _Other -> 549 measurement_server_loop(State) 550 end. 551 552%%---------------------------------------------------------------------- 553%% cpu_sup port program server wrapper 554%%---------------------------------------------------------------------- 555 556port_server_call(Pid, Command) -> 557 Pid ! {self(), Command}, 558 receive 559 {Pid, {data, Result}} -> Result; 560 {Pid, {error, Reason}} -> {error, Reason} 561 end. 562 563port_server_start_link() -> 564 Timeout = 6000, 565 Pid = spawn_link(fun() -> port_server_init(Timeout) end), 566 Pid ! {self(), ?ping}, 567 receive 568 {Pid, {data,4711}} -> {ok, Pid}; 569 {error,Reason} -> {error, Reason} 570 after Timeout -> 571 {error, timeout} 572 end. 573 574port_server_init(Timeout) -> 575 Port = start_portprogram(), 576 port_server_loop(Port, Timeout). 577 578port_server_loop(Port, Timeout) -> 579 receive 580 581 % Adjust timeout 582 {Pid, {timeout, Timeout}} -> 583 Pid ! {data, Timeout}, 584 port_server_loop(Port, Timeout); 585 % Number of processors 586 {Pid, ?nprocs} -> 587 port_command(Port, ?nprocs), 588 Result = port_receive_uint32(Port, Timeout), 589 Pid ! {self(), {data, Result}}, 590 port_server_loop(Port, Timeout); 591 592 % Average load for the past minute 593 {Pid, ?avg1} -> 594 port_command(Port, ?avg1), 595 Result = port_receive_uint32(Port, Timeout), 596 Pid ! {self(), {data, Result}}, 597 port_server_loop(Port, Timeout); 598 599 % Average load for the past five minutes 600 {Pid, ?avg5} -> 601 port_command(Port, ?avg5), 602 Result = port_receive_uint32(Port, Timeout), 603 Pid ! {self(), {data, Result}}, 604 port_server_loop(Port, Timeout); 605 606 % Average load for the past 15 minutes 607 {Pid, ?avg15} -> 608 port_command(Port, ?avg15), 609 Result = port_receive_uint32(Port, Timeout), 610 Pid ! {self(), {data, Result}}, 611 port_server_loop(Port, Timeout); 612 613 {Pid, ?util} -> 614 port_command(Port, ?util), 615 Result = port_receive_util(Port, Timeout), 616 Pid ! {self(), {data, Result}}, 617 port_server_loop(Port, Timeout); 618 619 % Port ping 620 {Pid, ?ping} -> 621 port_command(Port, ?ping), 622 Result = port_receive_uint32(Port, Timeout), 623 Pid ! {self(), {data, Result}}, 624 port_server_loop(Port, Timeout); 625 626 % Close port and this server 627 {Pid, ?quit} -> 628 port_command(Port, ?quit), 629 port_close(Port), 630 Pid ! {self(), {data, quit}}, 631 ok; 632 633 % Ignore other commands 634 _ -> port_server_loop(Port, Timeout) 635 end. 636 637port_receive_uint32( Port, Timeout) -> port_receive_uint32(Port, Timeout, []). 638port_receive_uint32(_Port, _Timeout, [D3,D2,D1,D0]) -> ?INT32(D3,D2,D1,D0); 639port_receive_uint32(_Port, _Timeout, [_,_,_,_ | G]) -> exit({port_garbage, G}); 640port_receive_uint32(Port, Timeout, D) -> 641 receive 642 {'EXIT', Port, Reason} -> exit({port_exit, Reason}); 643 {Port, {data, ND}} -> port_receive_uint32(Port, Timeout, D ++ ND) 644 after Timeout -> exit(timeout_uint32) end. 645 646port_receive_util(Port, Timeout) -> 647 receive 648 {Port, {data, [ NP3,NP2,NP1,NP0, % Number of processors 649 NE3,NE2,NE1,NE0 % Number of entries per processor 650 | CpuData]}} -> 651 port_receive_cpu_util( ?INT32(NP3,NP2,NP1,NP0), 652 ?INT32(NE3,NE2,NE1,NE0), 653 CpuData, []); 654 {'EXIT', Port, Reason} -> exit({port_exit, Reason}) 655 after Timeout -> exit(timeout_util) end. 656 657% per processor receive loop 658port_receive_cpu_util(0, _NE, [], CpuList) -> 659 % Return in ascending cpu_id order 660 lists:reverse(CpuList); 661port_receive_cpu_util(0, _NE, Garbage, _) -> 662 exit( {port_garbage, Garbage}); 663port_receive_cpu_util(NP, NE, CpuData, CpuList) -> 664 {CpuUtil, Rest} = port_receive_cpu_util_entries(NE, #cpu_util{}, CpuData), 665 port_receive_cpu_util(NP - 1, NE, Rest, [ CpuUtil | CpuList]). 666 667% per entry receive loop 668port_receive_cpu_util_entries(0, CU, Rest) -> 669 {CU, Rest}; 670port_receive_cpu_util_entries(NE, CU, 671 [ CID3, CID2, CID1, CID0, 672 Val3, Val2, Val1, Val0 | 673 CpuData]) -> 674 675 TagId = ?INT32(CID3,CID2,CID1,CID0), 676 Value = ?INT32(Val3,Val2,Val1,Val0), 677 678 % Conversions from integers to atoms 679 case TagId of 680 ?cu_cpu_id -> 681 NewCU = CU#cpu_util{cpu = Value}, 682 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 683 ?cu_user -> 684 NewCU = CU#cpu_util{ 685 busy = [{user, Value} | CU#cpu_util.busy] }, 686 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 687 ?cu_nice_user -> 688 NewCU = CU#cpu_util{ 689 busy = [{nice_user, Value} | CU#cpu_util.busy] }, 690 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 691 ?cu_kernel -> 692 NewCU = CU#cpu_util{ 693 busy = [{kernel, Value} | CU#cpu_util.busy] }, 694 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 695 ?cu_io_wait -> 696 NewCU = CU#cpu_util{ 697 non_busy = [{wait, Value} | CU#cpu_util.non_busy] }, 698 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 699 ?cu_idle -> 700 NewCU = CU#cpu_util{ 701 non_busy = [{idle, Value} | CU#cpu_util.non_busy] }, 702 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 703 ?cu_hard_irq -> 704 NewCU = CU#cpu_util{ 705 busy = [{hard_irq, Value} | CU#cpu_util.busy] }, 706 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 707 ?cu_soft_irq -> 708 NewCU = CU#cpu_util{ 709 busy = [{soft_irq, Value} | CU#cpu_util.busy] }, 710 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 711 ?cu_steal -> 712 NewCU = CU#cpu_util{ 713 non_busy = [{steal, Value} | CU#cpu_util.non_busy] }, 714 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 715 Unhandled -> 716 exit({unexpected_type_id, Unhandled}) 717 end; 718port_receive_cpu_util_entries(_, _, Data) -> 719 exit({data_mismatch, Data}). 720 721start_portprogram() -> 722 Port = os_mon:open_port("cpu_sup", [stream]), 723 port_command(Port, ?ping), 724 4711 = port_receive_uint32(Port, 5000), 725 Port. 726