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]). 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(), number()}] | number(). 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() -> number() | {'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%%---------------------------------------------------------------------- 203%% internal functions 204%%---------------------------------------------------------------------- 205 206get_uint32_measurement(Request, #internal{port = P, os_type = {unix, linux}}) -> 207 case file:open("/proc/loadavg",[read,raw]) of 208 {ok,F} -> 209 {ok,D} = file:read_line(F), 210 ok = file:close(F), 211 {ok,[Load1,Load5,Load15,_PRun,PTotal],_} = io_lib:fread("~f ~f ~f ~d/~d", D), 212 case Request of 213 ?avg1 -> sunify(Load1); 214 ?avg5 -> sunify(Load5); 215 ?avg15 -> sunify(Load15); 216 ?ping -> 4711; 217 ?nprocs -> PTotal 218 end; 219 {error,_} -> 220 port_server_call(P, Request) 221 end; 222get_uint32_measurement(Request, #internal{port = P, os_type = {unix, Sys}}) when 223 Sys == sunos; 224 Sys == dragonfly; 225 Sys == openbsd; 226 Sys == freebsd; 227 Sys == darwin -> 228 port_server_call(P, Request); 229get_uint32_measurement(Request, #internal{os_type = {unix, Sys}}) when Sys == irix64; 230 Sys == irix -> 231 %% Get the load average using uptime. 232 %% "8:01pm up 2 days, 22:12, 4 users, load average: 0.70, 0.58, 0.43" 233 D = os:cmd("uptime") -- "\n", 234 Avg = lists:reverse(hd(string:lexemes(lists:reverse(D), ":"))), 235 {ok, [L1, L5, L15], _} = io_lib:fread("~f, ~f, ~f", Avg), 236 case Request of 237 ?avg1 -> sunify(L1); 238 ?avg5 -> sunify(L5); 239 ?avg15 -> sunify(L15); 240 ?ping -> 4711; 241 ?nprocs -> 242 {ok, ProcList} = file:list_dir("/proc/pinfo"), 243 length(ProcList) 244 end; 245get_uint32_measurement(_, _) -> 246 throw(not_implemented). 247 248 249get_util_measurement(?util, #internal{port = P }) -> 250 case port_server_call(P, ?util) of 251 {error, Error} -> {error, Error}; 252 NewCpuUtil -> NewCpuUtil 253 end; 254get_util_measurement(_,_) -> 255 throw(not_implemented). 256 257%%---------------------------------------------------------------------- 258%% BEGIN: tainted internal functions 259%%---------------------------------------------------------------------- 260 261sunify(Val) -> 262 round(Val*256). % Note that Solaris and Linux load averages are 263 % measured quite differently anyway 264 265 266keysearchdelete(_, _, []) -> 267 {false, []}; 268keysearchdelete(K, N, [T|Ts]) when element(N, T) == K -> 269 {{value, T}, Ts}; 270keysearchdelete(K, N, [T|Ts]) -> 271 {X, NTs} = keysearchdelete(K, N, Ts), 272 {X, [T|NTs]}. 273 274%% Internal cpu utilization functions 275 276%% cpu_util_diff(New, Old) takes a list of new cpu_util records as first 277%% argument and a list of old cpu_util records as second argument. The 278%% two lists have to be sorted on cpu index in ascending order. 279%% 280%% The returned value is a difference list in descending order. 281cpu_util_diff(New, Old) -> 282 cpu_util_diff(New, Old, []). 283 284cpu_util_diff([], [], Acc) -> 285 Acc; 286cpu_util_diff([#cpu_util{cpu = Cpu, 287 busy = NewBusy, 288 non_busy = NewNonBusy} | NewCpuUtils], 289 [#cpu_util{cpu = Cpu, 290 busy = OldBusy, 291 non_busy = OldNonBusy} | OldCpuUtils], 292 Acc) -> 293 {PreBusy, GotBusy} = state_list_diff(NewBusy, OldBusy), 294 {NonBusy, GotNonBusy} = state_list_diff(NewNonBusy, OldNonBusy), 295 Busy = case GotBusy orelse GotNonBusy of 296 true -> 297 PreBusy; 298 false -> 299 %% This can happen if cpu_sup:util/[0,1] is called 300 %% again immediately after the previous call has 301 %% returned. Because the user obviously is doing 302 %% something we charge "user". 303 lists:map(fun ({user, 0}) -> {user, 1}; 304 ({_, 0} = StateTup) -> StateTup 305 end, 306 PreBusy) 307 end, 308cpu_util_diff(NewCpuUtils, OldCpuUtils, [#cpu_util{cpu = Cpu, 309 busy = Busy, 310 non_busy = NonBusy} 311 | Acc]); 312 313%% A new cpu appeared 314cpu_util_diff([#cpu_util{cpu = NC}|_] = New, 315 [#cpu_util{cpu = OC}|_] = Old, 316 Acc) when NC < OC -> 317cpu_util_diff(New, [#cpu_util{cpu = NC}|Old], Acc); 318cpu_util_diff([#cpu_util{cpu = NC}|_] = New, [], Acc) -> 319cpu_util_diff(New, [#cpu_util{cpu = NC}], Acc); 320 321%% An old cpu disappeared 322cpu_util_diff([#cpu_util{cpu = NC}|Ns], 323 [#cpu_util{cpu = OC}|_] = Old, 324 Acc) when NC > OC -> 325cpu_util_diff(Ns, Old, Acc); 326cpu_util_diff([], _Old, Acc) -> 327cpu_util_diff([], [], Acc). 328 329cpu_util_rel(NewCpuUtils, OldCpuUtils, Detailed, PerCpu) -> 330 cpu_util_rel(cpu_util_diff(NewCpuUtils, OldCpuUtils), Detailed, PerCpu). 331 332%% 333%% cpu_util_rel/3 takes a difference list of cpu_util records as first 334%% argument, a boolean determining if the result should be detailed as 335%% second argument, and a boolean determining if the result should be 336%% per cpu as third argument. The first argument (the difference list) 337%% has to be sorted on cpu index in descending order. 338%% 339cpu_util_rel(CUDiff, false, false) -> 340 {B, T} = lists:foldl(fun (#cpu_util{busy = BusyList, 341 non_busy = NonBusyList}, 342 {BusyAcc, TotAcc}) -> 343 Busy = state_list_sum(BusyList), 344 NonBusy = state_list_sum(NonBusyList), 345 {BusyAcc+Busy, TotAcc+Busy+NonBusy} 346 end, 347 {0, 0}, 348 CUDiff), 349 BRel = B/T*100, 350 {all, BRel, 100-BRel, []}; 351cpu_util_rel(CUDiff, true, false) -> 352 cpu_util_rel_det(CUDiff, #cpu_util{cpu = [], busy = [], non_busy = []}); 353cpu_util_rel(CUDiff, false, true) -> 354 cpu_util_rel_pcpu(CUDiff, []); 355cpu_util_rel(CUDiff, true, true) -> 356 cpu_util_rel_det_pcpu(CUDiff, []). 357 358cpu_util_rel_pcpu([], Acc) -> 359 Acc; 360cpu_util_rel_pcpu([#cpu_util{cpu = C, 361 busy = BusyList, 362 non_busy = NonBusyList} | Rest], Acc) -> 363 Busy = state_list_sum(BusyList), 364 NonBusy = state_list_sum(NonBusyList), 365 Tot = Busy + NonBusy, 366 cpu_util_rel_pcpu(Rest, [{C, Busy/Tot*100, NonBusy/Tot*100, []}|Acc]). 367 368cpu_util_rel_det([], #cpu_util{cpu = CpuAcc, 369 busy = BusyAcc, 370 non_busy = NonBusyAcc}) -> 371 Total = state_list_sum(BusyAcc) + state_list_sum(NonBusyAcc), 372 {CpuAcc, mk_rel_states(BusyAcc,Total), mk_rel_states(NonBusyAcc,Total), []}; 373cpu_util_rel_det([#cpu_util{cpu = Cpu, 374 busy = Busy, 375 non_busy = NonBusy} | Rest], 376 #cpu_util{cpu = CpuAcc, 377 busy = BusyAcc, 378 non_busy = NonBusyAcc}) -> 379 cpu_util_rel_det(Rest, #cpu_util{cpu = [Cpu|CpuAcc], 380 busy = state_list_add(Busy, 381 BusyAcc), 382 non_busy = state_list_add(NonBusy, 383 NonBusyAcc)}). 384 385cpu_util_rel_det_pcpu([], Acc) -> 386 Acc; 387cpu_util_rel_det_pcpu([#cpu_util{cpu = Cpu, 388 busy = Busy, 389 non_busy = NonBusy}| Rest], Acc) -> 390 Total = state_list_sum(Busy) + state_list_sum(NonBusy), 391 cpu_util_rel_det_pcpu(Rest, 392 [{Cpu, 393 mk_rel_states(Busy, Total), 394 mk_rel_states(NonBusy, Total), 395 []} | Acc]). 396 397mk_rel_states(States, Total) -> 398 lists:map(fun ({State, Value}) -> {State, 100*Value/Total} end, States). 399 400state_list_sum(StateList) -> 401 lists:foldl(fun ({_, X}, Acc) -> Acc+X end, 0, StateList). 402 403state_list_diff([],[]) -> 404 {[], false}; 405state_list_diff([{State,ValueNew}|RestNew], []) -> 406 state_list_diff([{State, ValueNew} | RestNew], [{State, 0}]); 407state_list_diff([{State,ValueNew}|RestNew], [{State,ValueOld}|RestOld]) -> 408 ValDiff = val_diff(State, ValueNew, ValueOld), 409 {RestStateDiff, FoundDiff} = state_list_diff(RestNew, RestOld), 410 {[{State, ValDiff} | RestStateDiff], FoundDiff orelse ValDiff /= 0}. 411 412state_list_add([],[]) -> 413 []; 414state_list_add([{State, ValueA}|RestA], []) -> 415 [{State, ValueA} | state_list_add(RestA, [])]; 416state_list_add([{State, ValueA} | RestA], [{State, ValueB} | RestB]) -> 417 [{State, ValueA + ValueB} | state_list_add(RestA, RestB)]. 418 419one_step_backwards(State, New, Old) -> 420 case os:type() of 421 {unix, linux} -> 422 %% This should never happen! But values sometimes takes a step 423 %% backwards on linux. We'll ignore it as long as it's only 424 %% one step... 425 0; 426 _ -> 427 val_diff2(State, New, Old) 428 end. 429 430val_diff(State, New, Old) when New == Old - 1 -> 431 one_step_backwards(State, New, Old); 432val_diff(State, ?MAX_UINT32, 0) -> 433 one_step_backwards(State, ?MAX_UINT32, 0); 434val_diff(State, New, Old) -> 435 val_diff2(State, New, Old). 436 437val_diff2(State, New, Old) when New > ?MAX_UINT32; Old > ?MAX_UINT32 -> 438 %% We obviously got uints > 32 bits 439 ensure_positive_diff(State, New - Old); 440val_diff2(State, New, Old) when New < Old -> 441 %% 32-bit integer wrapped 442 ensure_positive_diff(State, (?MAX_UINT32 + 1) + New - Old); 443val_diff2(_State, New, Old) -> 444 New - Old. 445 446ensure_positive_diff(_State, Diff) when Diff >= 0 -> 447 Diff; 448ensure_positive_diff(State, Diff) -> 449 throw({error, {negative_diff, State, Diff}}). 450%%---------------------------------------------------------------------- 451%% END: tainted internal functions 452%%---------------------------------------------------------------------- 453 454%%---------------------------------------------------------------------- 455%% cpu_sup measurement server wrapper 456%%---------------------------------------------------------------------- 457 458measurement_server_call(Pid, Request) -> 459 Timeout = 5000, 460 Pid ! {self(), Request}, 461 receive 462 {data, Data} -> Data 463 after Timeout -> 464 {error, timeout} 465 end. 466 467measurement_server_restart(Pid) -> 468 exit(Pid, kill), 469 measurement_server_start(). 470 471measurement_server_start() -> 472 spawn(fun() -> measurement_server_init() end). 473 474measurement_server_init() -> 475 process_flag(trap_exit, true), 476 OS = os:type(), 477 Server = case OS of 478 {unix, Flavor} when 479 Flavor==sunos; 480 Flavor==linux; 481 Flavor==darwin; 482 Flavor==freebsd; 483 Flavor==dragonfly; 484 Flavor==openbsd -> 485 {ok, Pid} = port_server_start_link(), 486 Pid; 487 {unix, Flavor} when 488 Flavor==irix64; 489 Flavor==irix -> 490 not_used; 491 _ -> 492 exit({unsupported_os, OS}) 493 end, 494 measurement_server_loop(#internal{port=Server, os_type=OS}). 495 496measurement_server_loop(State) -> 497 receive 498 {_, quit} -> 499 State#internal.port ! {self(), ?quit}, 500 ok; 501 {'DOWN',Monitor,process,_,_} -> 502 measurement_server_loop(State#internal{ util = lists:keydelete( 503 Monitor, 504 2, 505 State#internal.util)}); 506 {Pid, {?util, D, PC, Client}} -> 507 {Monitor, OldCpuUtil, Utils2} = case keysearchdelete(Client, 1, State#internal.util) of 508 {{value, {Client, Mon, U}}, Us} -> {Mon, U, Us}; 509 {false, Us} -> {erlang:monitor(process, Client), [], Us} 510 end, 511 try get_util_measurement(?util, State) of 512 NewCpuUtil -> 513 Result = cpu_util_rel(NewCpuUtil, OldCpuUtil, D, PC), 514 Pid ! {data, Result}, 515 measurement_server_loop(State#internal{util=[{Client,Monitor,NewCpuUtil}|Utils2]}) 516 catch 517 Error -> 518 Pid ! {error, Error}, 519 measurement_server_loop(State) 520 end; 521 {Pid, Request} -> 522 _ = try get_uint32_measurement(Request, State) of 523 Result -> Pid ! {data, Result} 524 catch 525 Error -> Pid ! {error, Error} 526 end, 527 measurement_server_loop(State); 528 {'EXIT', OldPid, _n} when State#internal.port == OldPid -> 529 {ok, NewPid} = port_server_start_link(), 530 measurement_server_loop(State#internal{port = NewPid}); 531 _Other -> 532 measurement_server_loop(State) 533 end. 534 535%%---------------------------------------------------------------------- 536%% cpu_sup port program server wrapper 537%%---------------------------------------------------------------------- 538 539port_server_call(Pid, Command) -> 540 Pid ! {self(), Command}, 541 receive 542 {Pid, {data, Result}} -> Result; 543 {Pid, {error, Reason}} -> {error, Reason} 544 end. 545 546port_server_start_link() -> 547 Timeout = 6000, 548 Pid = spawn_link(fun() -> port_server_init(Timeout) end), 549 Pid ! {self(), ?ping}, 550 receive 551 {Pid, {data,4711}} -> {ok, Pid}; 552 {error,Reason} -> {error, Reason} 553 after Timeout -> 554 {error, timeout} 555 end. 556 557port_server_init(Timeout) -> 558 Port = start_portprogram(), 559 port_server_loop(Port, Timeout). 560 561port_server_loop(Port, Timeout) -> 562 receive 563 564 % Adjust timeout 565 {Pid, {timeout, Timeout}} -> 566 Pid ! {data, Timeout}, 567 port_server_loop(Port, Timeout); 568 % Number of processors 569 {Pid, ?nprocs} -> 570 port_command(Port, ?nprocs), 571 Result = port_receive_uint32(Port, Timeout), 572 Pid ! {self(), {data, Result}}, 573 port_server_loop(Port, Timeout); 574 575 % Average load for the past minute 576 {Pid, ?avg1} -> 577 port_command(Port, ?avg1), 578 Result = port_receive_uint32(Port, Timeout), 579 Pid ! {self(), {data, Result}}, 580 port_server_loop(Port, Timeout); 581 582 % Average load for the past five minutes 583 {Pid, ?avg5} -> 584 port_command(Port, ?avg5), 585 Result = port_receive_uint32(Port, Timeout), 586 Pid ! {self(), {data, Result}}, 587 port_server_loop(Port, Timeout); 588 589 % Average load for the past 15 minutes 590 {Pid, ?avg15} -> 591 port_command(Port, ?avg15), 592 Result = port_receive_uint32(Port, Timeout), 593 Pid ! {self(), {data, Result}}, 594 port_server_loop(Port, Timeout); 595 596 {Pid, ?util} -> 597 port_command(Port, ?util), 598 Result = port_receive_util(Port, Timeout), 599 Pid ! {self(), {data, Result}}, 600 port_server_loop(Port, Timeout); 601 602 % Port ping 603 {Pid, ?ping} -> 604 port_command(Port, ?ping), 605 Result = port_receive_uint32(Port, Timeout), 606 Pid ! {self(), {data, Result}}, 607 port_server_loop(Port, Timeout); 608 609 % Close port and this server 610 {Pid, ?quit} -> 611 port_command(Port, ?quit), 612 port_close(Port), 613 Pid ! {self(), {data, quit}}, 614 ok; 615 616 % Ignore other commands 617 _ -> port_server_loop(Port, Timeout) 618 end. 619 620port_receive_uint32( Port, Timeout) -> port_receive_uint32(Port, Timeout, []). 621port_receive_uint32(_Port, _Timeout, [D3,D2,D1,D0]) -> ?INT32(D3,D2,D1,D0); 622port_receive_uint32(_Port, _Timeout, [_,_,_,_ | G]) -> exit({port_garbage, G}); 623port_receive_uint32(Port, Timeout, D) -> 624 receive 625 {'EXIT', Port, Reason} -> exit({port_exit, Reason}); 626 {Port, {data, ND}} -> port_receive_uint32(Port, Timeout, D ++ ND) 627 after Timeout -> exit(timeout_uint32) end. 628 629port_receive_util(Port, Timeout) -> 630 receive 631 {Port, {data, [ NP3,NP2,NP1,NP0, % Number of processors 632 NE3,NE2,NE1,NE0 % Number of entries per processor 633 | CpuData]}} -> 634 port_receive_cpu_util( ?INT32(NP3,NP2,NP1,NP0), 635 ?INT32(NE3,NE2,NE1,NE0), 636 CpuData, []); 637 {'EXIT', Port, Reason} -> exit({port_exit, Reason}) 638 after Timeout -> exit(timeout_util) end. 639 640% per processor receive loop 641port_receive_cpu_util(0, _NE, [], CpuList) -> 642 % Return in ascending cpu_id order 643 lists:reverse(CpuList); 644port_receive_cpu_util(0, _NE, Garbage, _) -> 645 exit( {port_garbage, Garbage}); 646port_receive_cpu_util(NP, NE, CpuData, CpuList) -> 647 {CpuUtil, Rest} = port_receive_cpu_util_entries(NE, #cpu_util{}, CpuData), 648 port_receive_cpu_util(NP - 1, NE, Rest, [ CpuUtil | CpuList]). 649 650% per entry receive loop 651port_receive_cpu_util_entries(0, CU, Rest) -> 652 {CU, Rest}; 653port_receive_cpu_util_entries(NE, CU, 654 [ CID3, CID2, CID1, CID0, 655 Val3, Val2, Val1, Val0 | 656 CpuData]) -> 657 658 TagId = ?INT32(CID3,CID2,CID1,CID0), 659 Value = ?INT32(Val3,Val2,Val1,Val0), 660 661 % Conversions from integers to atoms 662 case TagId of 663 ?cu_cpu_id -> 664 NewCU = CU#cpu_util{cpu = Value}, 665 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 666 ?cu_user -> 667 NewCU = CU#cpu_util{ 668 busy = [{user, Value} | CU#cpu_util.busy] }, 669 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 670 ?cu_nice_user -> 671 NewCU = CU#cpu_util{ 672 busy = [{nice_user, Value} | CU#cpu_util.busy] }, 673 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 674 ?cu_kernel -> 675 NewCU = CU#cpu_util{ 676 busy = [{kernel, Value} | CU#cpu_util.busy] }, 677 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 678 ?cu_io_wait -> 679 NewCU = CU#cpu_util{ 680 non_busy = [{wait, Value} | CU#cpu_util.non_busy] }, 681 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 682 ?cu_idle -> 683 NewCU = CU#cpu_util{ 684 non_busy = [{idle, Value} | CU#cpu_util.non_busy] }, 685 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 686 ?cu_hard_irq -> 687 NewCU = CU#cpu_util{ 688 busy = [{hard_irq, Value} | CU#cpu_util.busy] }, 689 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 690 ?cu_soft_irq -> 691 NewCU = CU#cpu_util{ 692 busy = [{soft_irq, Value} | CU#cpu_util.busy] }, 693 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 694 ?cu_steal -> 695 NewCU = CU#cpu_util{ 696 non_busy = [{steal, Value} | CU#cpu_util.non_busy] }, 697 port_receive_cpu_util_entries(NE - 1, NewCU, CpuData); 698 Unhandled -> 699 exit({unexpected_type_id, Unhandled}) 700 end; 701port_receive_cpu_util_entries(_, _, Data) -> 702 exit({data_mismatch, Data}). 703 704start_portprogram() -> 705 Port = os_mon:open_port("cpu_sup", [stream]), 706 port_command(Port, ?ping), 707 4711 = port_receive_uint32(Port, 5000), 708 Port. 709