Module: Process::Metrics::General::ProcessStatus
- Defined in:
- lib/process/metrics/general/process_status.rb
Overview
General process information via the process status command (ps). Used on non-Linux platforms (e.g. Darwin) where there is no /proc; ps is the portable way to get pid, ppid, times, and memory in one pass.
Constant Summary collapse
- PS =
"ps"- FIELDS =
The fields that will be extracted from the
pscommand (order matches -o output). { pid: ->(value){value.to_i}, ppid: ->(value){value.to_i}, pgid: ->(value){value.to_i}, pcpu: ->(value){value.to_f}, vsz: ->(value){value.to_i * 1024}, rss: ->(value){value.to_i * 1024}, time: Process::Metrics.method(:duration), etime: Process::Metrics.method(:duration), command: ->(value){value}, }
Class Method Summary collapse
-
.capture(pid: nil, ppid: nil, memory: Memory.supported?) ⇒ Object
Capture process information using ps.
-
.supported? ⇒ Boolean
Whether process listing via ps is available on this system.
Class Method Details
.capture(pid: nil, ppid: nil, memory: Memory.supported?) ⇒ Object
Capture process information using ps. If given a pid, captures that process; if given ppid, captures that process and all descendants. Specify both to capture a process and its children.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/process/metrics/general/process_status.rb', line 36 def self.capture(pid: nil, ppid: nil, memory: Memory.supported?) spawned_pid = nil header, *lines = IO.pipe do |input, output| arguments = [PS] # When filtering by ppid we need the full process list to build the tree, so use "ax"; otherwise limit to -p. if pid && ppid.nil? arguments.push("-p", Array(pid).join(",")) else arguments.push("ax") end arguments.push("-o", FIELDS.keys.join(",")) spawned_pid = Process.spawn(*arguments, out: output) output.close input.readlines.map(&:strip) ensure input.close # Always kill and reap the ps subprocess so we never leave it hanging if the pipe closes early. if spawned_pid begin Process.kill(:KILL, spawned_pid) Process.wait(spawned_pid) rescue => error warn "Failed to cleanup ps process #{spawned_pid}:\n#{error.}" end end end processes = {} lines.each do |line| next if line.empty? values = line.split(/\s+/, FIELDS.size) next if values.size < FIELDS.size record = FIELDS.keys.map.with_index{|key, i| FIELDS[key].call(values[i])} instance = General.new(*record, nil) processes[instance.process_id] = instance end # Restrict to the requested pid/ppid subtree; exclude our own ps process from the result. if ppid pids = Set.new hierarchy = General.build_tree(processes) General.(Array(pid), hierarchy, pids) if pid General.(Array(ppid), hierarchy, pids) processes.select!{|process_id, _| process_id != spawned_pid && pids.include?(process_id)} else processes.delete(spawned_pid) if spawned_pid end General.capture_memory(processes) if memory processes end |
.supported? ⇒ Boolean
Whether process listing via ps is available on this system.
27 28 29 |
# File 'lib/process/metrics/general/process_status.rb', line 27 def self.supported? system("which", PS, out: File::NULL, err: File::NULL) end |