%% -*- erlang-indent-level: 2 -*- %%%------------------------------------------------------------------- %%% File : prfSys.erl %%% Author : Mats Cronqvist %%% Description : prf collector of system info %%% %%% Created : 5 Dec 2003 by Mats Cronqvist %%%------------------------------------------------------------------- %% collects info about the OS and the Erlang system. %% * emulator info %% call the BIFs memory/0, statistics/1 and system_info/1 %% * OS info %% if OS is Linux 2.6 or greater; %% - read from /proc/stat, /proc/meminfo (and /proc/net/dev ?) %% - read from /proc/self/stat (and /proc/self/statm ?) %% elseif os_mon is started; %% - call memsup:get_memory_data/0 and cpu_sup:cpu_sup:util([detailed]) %% else if OS is unix %% - run the ps command in a port %% else %% - return an empty list %% %% returns a list of tagged tuples %% %% tag [unit] source %% node [atom()] erlang:node() %% now [now()] prfTime:ts() %% procs [count] erlang:system_info(process_count) %% context_switches [count/s] erlang:statistics(context_switches) %% gcs [count/s] erlang:statistics(garbage_collection) %% gc_reclaimed [byte/s] erlang:statistics(garbage_collection) %% io_in [byte/s] erlang:statistics(io) %% io_out [byte/s] erlang:statistics(io) %% reductions [count/s] erlang:statistics(reductions) %% run_queue [count] erlang:statistics(run_queue) %% total [byte] erlang:memory() %% processes [byte] erlang:memory() %% processes_used [byte] erlang:memory() %% system [byte] erlang:memory() %% atom [byte] erlang:memory() %% atom_used [byte] erlang:memory() %% binary [byte] erlang:memory() %% code [byte] erlang:memory() %% ets [byte] erlang:memory() %% user [frac] /proc/stat %% nice [frac] /proc/stat %% kernel [frac] /proc/stat %% idle [frac] /proc/stat %% iowait [frac] /proc/stat %% ctxt [frac] /proc/stat %% beam_user, [frac] /proc/self/stat %% beam_kernel, [frac] /proc/self/stat %% beam_vss [byte] /proc/self/stat %% beam_rss [pages] /proc/self/stat %% beam_minflt [count/s] /proc/self/stat %% beam_majflt [count/s] /proc/self/stat %% total_ram [byte] /proc/meminfo %% f(C),f(Data),{C,Data}=prfSys:collect(Cst),f(Cst),Cst=C,Data. -module(prfSys). -export([collect/1,config/2]). -record(cst,{strategy=strategy(), node=node(), total_ram=0, cores=1, cache=[], now=prfTime:ts()}). -define(RATES,[context_switches,gcs,gc_reclaimed,io_in,io_out,reductions, user,nice,kernel,idle,iowait, beam_user,beam_kernel,beam_minflt,beam_majflt]). %%% returns {State, Data} collect(init) -> collect(init_cst(#cst{})); collect(Cst) -> Data = data(Cst), {new_cst(Cst,Data), {?MODULE, rates(Cst,Data)}}. config(State,_ConfigData) -> State. data(Cst) -> constants(Cst)++stats()++os_info(Cst#cst.strategy). constants(#cst{node=Node,total_ram=Total_ram,cores=Cores}) -> [{node, Node},{total_ram,Total_ram},{cores,Cores}]. stats() -> Procs = erlang:system_info(process_count), {Ctxt,0} = erlang:statistics(context_switches), {GCs,GCwords,0} = erlang:statistics(garbage_collection), {{input,IoIn},{output,IoOut}} = erlang:statistics(io), {Reds,_} = erlang:statistics(reductions), RunQ = erlang:statistics(run_queue), [{now,prfTime:ts()}, {procs,Procs}, {context_switches,Ctxt}, {gcs,GCs}, {gc_reclaimed,GCwords*4}, {io_in,IoIn}, {io_out,IoOut}, {reductions,Reds}, {run_queue,RunQ} | erlang:memory()]. new_cst(Cst,Data) -> Cst#cst{cache=[D || {Tag,_}=D <- Data, lists:member(Tag,?RATES)], now=lks(now,Data)}. rates(#cst{cache=Cache,now=Now},Data) -> [diff(Cache,D,delta_t(Now,Data)) || D <- Data]. diff(Cache,{Tag,Val},DeltaT)-> try {Tag,(Val-lks(Tag,Cache))/DeltaT} catch no_tag -> {Tag,Val}; _:_ -> {Tag,0} end. delta_t(Then,Data) -> timer:now_diff(lks(now,Data),Then)/1000000. lks(Tag,List) -> case lists:keysearch(Tag,1,List) of {value,{Tag,Val}} -> Val; false -> throw(no_tag) end. init_cst(Cst = #cst{strategy={linux,_}}) -> Cst#cst{total_ram = total_ram(), cores = cores(Cst#cst.strategy)}; init_cst(Cst) -> Cst. strategy() -> Os_mon_p = [ok||{os_mon,_,_}<-application:which_applications()], case os:type() of {unix,linux} -> {linux,init_linux()}; _ when Os_mon_p == [ok] -> {os_mon,[]}; {unix,_} -> {ps,init_ps()}; _ -> {none,[]} end. %% OS info %% only the 'linux' (i.e. linux 2.6 or higher) strategy implemented -record(fds,{proc_stat,proc_self_stat}). os_info({linux,#fds{proc_stat=FDs,proc_self_stat=FDss}}) -> proc_stat(FDs)++proc_self_stat(FDss); os_info(_) -> []. proc_stat(FDs) -> %%user nice kernel idle iowait irq softirq steal {ok,Str} = file:pread(FDs,0,200), [User,Nice,Kernel,Idle,Iowait] = case string:tokens(Str," \n") of ["cpu",I1,I2,I3,I4,I5|_] -> [I1,I2,I3,I4,I5]; _ -> [0,0,0,0,0] end, lists:zip([user,nice,kernel,idle,iowait], [to_sec(J) || J <- [User,Nice,Kernel,Idle,Iowait]]). proc_self_stat(FDss) -> %%% pid,comm,state,ppid,pgrp,session,tty_nr,tpgid,flags, %%% minflt,cminflt,majflt,cmajflt,utime,stime,cutime,cstime, %%% priority,nice,num_threads,itrealvalue,starttime,vsize,rss {ok,Str} = file:pread(FDss,0,200), {Minflt,Majflt,Utime,Stime,Vsize,Rss} = case string:tokens(Str," ") of [_,_,_,_,_,_,_,_,_,I10,_,I12,_,I14,I15,_,_,_,_,_,_,_,I23,I24|_] -> {I10,I12,I14,I15,I23,I24}; _ -> {0,0,0,0,0,0} end, lists:zip([beam_user,beam_kernel,beam_vss,beam_rss,beam_minflt,beam_majflt], [to_sec(Utime),to_sec(Stime),to_int(Vsize), to_int(Rss), %% in pages... to_int(Minflt),to_int(Majflt)]). to_sec(J) -> to_int(J)/100. %should use a better transform jiffies->secs to_int(J) -> list_to_integer(J). init_linux() -> {ok,FDs} = file:open("/proc/stat",[read,raw]), {ok,FDss} = file:open("/proc/self/stat",[read,raw]), #fds{proc_stat=FDs,proc_self_stat=FDss}. cores({linux,#fds{proc_stat=Proc_stat}}) -> {ok,Str} = file:pread(Proc_stat,0,1000), Toks = string:tokens(Str,"\n"), case length(lists:takewhile(fun(S)->lists:prefix("cpu",S) end,Toks)) of 1 -> 1; M -> M-1 end; cores(_) -> 1. total_ram() -> case file:open("/proc/meminfo",[read,raw]) of {ok,FD} -> try {ok,Str} = file:pread(FD,0,30), ["MemTotal:",T,"kB"|_] = string:tokens(Str," \n"), list_to_integer(T)*1024 catch _:_ -> 0 after file:close(FD) end; _ -> 0 end. init_ps() -> [].