require 'celluloid/current' class Eye::SystemResources # cached system resources class << self def memory(pid) if mem = cache.proc_mem(pid) mem.resident end end def cpu(pid) if cpu = cache.proc_cpu(pid) cpu.percent * 100 end end def children(parent_pid) cache.children(parent_pid) end def start_time(pid) # unixtime if cpu = cache.proc_cpu(pid) cpu.start_time.to_i / 1000 end end # total cpu usage in seconds def cputime(pid) if cpu = cache.proc_cpu(pid) cpu.total.to_f / 1000 end end # last child in a children tree def leaf_child(pid) if dc = deep_children(pid) dc.detect do |child| args = Eye::Sigar.proc_args(child)[0] rescue '' !args.start_with?('logger') && child != pid end end end def deep_children(pid) Array(pid_or_children(pid)).flatten.sort_by(&:-@) end def pid_or_children(pid) c = children(pid) if !c || c.empty? pid else c.map { |ppid| pid_or_children(ppid) } end end def args(pid) Eye::Sigar.proc_args(pid).join(' ').strip rescue '-' end def resources(pid) { memory: memory(pid), cpu: cpu(pid), start_time: start_time(pid), pid: pid } end def cache Celluloid::Actor[:system_resources_cache] end end class Cache include Celluloid attr_reader :expire def initialize clear setup_expire end def setup_expire(expire = 5) @expire = expire @timer.cancel if @timer @timer = every(@expire) { clear } end def clear @memory = {} @cpu = {} @ppids = {} end def proc_mem(pid) @memory[pid] ||= Eye::Sigar.proc_mem(pid) if pid rescue ArgumentError # when incorrect PID, just skip end def proc_cpu(pid) @cpu[pid] ||= Eye::Sigar.proc_cpu(pid) if pid rescue ArgumentError # when incorrect PID, just skip end def children(pid) if pid @ppids[pid] ||= Eye::Sigar.proc_list("State.Ppid.eq=#{pid}") else [] end end end # Setup global sigar singleton here Cache.supervise(as: :system_resources_cache) end