Class: Process::Metrics::Memory::Linux

Inherits:
Object
  • Object
show all
Defined in:
lib/process/metrics/memory/linux.rb

Overview

Linux implementation of per-process memory metrics using /proc/[pid]/smaps or /proc/[pid]/smaps_rollup, and /proc/[pid]/stat for fault counters. Prefers smaps_rollup when readable (single summary); otherwise falls back to full smaps and counts maps from /proc/[pid]/maps.

Constant Summary collapse

SMAP =

Mapping from smaps/smaps_rollup line names to Memory struct members (values in kB, converted to bytes when parsing).

{
  "Rss" => :resident_size,
  "Pss" => :proportional_size,
  "Shared_Clean" => :shared_clean_size,
  "Shared_Dirty" => :shared_dirty_size,
  "Private_Clean" => :private_clean_size,
  "Private_Dirty" => :private_dirty_size,
  "Referenced" => :referenced_size,
  "Anonymous" => :anonymous_size,
  "Swap" => :swap_size,
  "SwapPss" => :proportional_swap_size,
}

Class Method Summary collapse

Class Method Details

.capture(pid, faults: true, **options) ⇒ Object

Capture memory usage from /proc/[pid]/smaps (and map count from VmFlags) and /proc/[pid]/maps. Optionally fill fault counters from /proc/[pid]/stat.



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
# File 'lib/process/metrics/memory/linux.rb', line 51

def self.capture(pid, faults: true, **options)
  File.open("/proc/#{pid}/smaps_rollup") do |file|
    usage = Memory.zero
    file.each_line do |line|
      if /(?<name>.*?):\s+(?<value>\d+) kB/ =~ line
        if key = SMAP[name]
          # Convert from kilobytes to bytes:
          usage[key] += value.to_i * 1024
        end
      end
    end
    
    usage.map_count += File.readlines("/proc/#{pid}/maps").size
    
    # Also capture fault counters if requested:
    if faults
      self.capture_faults(pid, usage)
    end
    
    return usage
  end
rescue Errno::ENOENT, Errno::ESRCH, Errno::EACCES
  # Process doesn't exist or we can't access it.
  return nil
end

.capture_faults(pid, usage) ⇒ Object

Extract minor and major page fault counters from /proc/[pid]/stat (proc(5): fields 10=minflt, 12=majflt) and assign to usage.



14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/process/metrics/memory/linux.rb', line 14

def self.capture_faults(pid, usage)
  stat_content = File.read("/proc/#{pid}/stat")
  # The comm field can contain spaces and parentheses; find the closing ')':
  closing_paren_index = stat_content.rindex(")")
  return unless closing_paren_index
  fields = stat_content[(closing_paren_index + 2)..].split(/\s+/)
  # proc(5): field 10=minflt, 12=majflt; our fields array is 0-indexed from field 3.
  usage.minor_faults = fields[10-3].to_i
  usage.major_faults = fields[12-3].to_i
rescue
  # Ignore.
end

.supported?Boolean

Whether the memory usage can be captured on this system.

Returns:

  • (Boolean)


43
44
45
# File 'lib/process/metrics/memory/linux.rb', line 43

def self.supported?
  true
end