Class: Process::Metrics::Memory::Darwin
- Inherits:
-
Object
- Object
- Process::Metrics::Memory::Darwin
- Defined in:
- lib/process/metrics/memory/darwin.rb
Overview
Darwin (macOS) implementation of per-process memory metrics using vmmap(1). Parses vmmap output for virtual/resident/dirty/swap per region and maps sharing mode (PRV, COW, SHM) to private/shared fields.
Constant Summary collapse
- VMMAP =
"/usr/bin/vmmap"- LINE =
Regex for vmmap region lines: region name, address range, [virtual resident dirty swap], permissions, SM=sharing_mode.
/\A \s* (?<region_name>.+?)\s+ (?<start_address>[0-9a-fA-F]+)-(?<end_address>[0-9a-fA-F]+)\s+ \[\s*(?<virtual_size>[\d\.]+[KMG]?)\s+(?<resident_size>[\d\.]+[KMG]?)\s+(?<dirty_size>[\d\.]+[KMG]?)\s+(?<swap_size>[\d\.]+[KMG]?)\s*\]\s+ (?<permissions>[rwx\-\/]+)\s+ SM=(?<sharing_mode>\w+) /x
Class Method Summary collapse
-
.capture(pid, count: 1, **options) ⇒ Object
Capture memory usage by running vmmap for the given pid and summing region sizes.
-
.parse_size(size_string) ⇒ Object
Parse a size string from vmmap (e.g. "4K", "1.5M", "2G") into bytes.
-
.supported? ⇒ Boolean
Whether the memory usage can be captured on this system.
Class Method Details
.capture(pid, count: 1, **options) ⇒ Object
Capture memory usage by running vmmap for the given pid and summing region sizes. Proportional size is estimated as resident_size / count (Darwin has no PSS).
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 |
# File 'lib/process/metrics/memory/darwin.rb', line 46 def self.capture(pid, count: 1, **) IO.popen(["vmmap", pid.to_s], "r") do |io| usage = Memory.zero io.each_line do |line| if match = LINE.match(line) usage.map_count += 1 virtual_size = parse_size(match[:virtual_size]) resident_size = parse_size(match[:resident_size]) dirty_size = parse_size(match[:dirty_size]) swap_size = parse_size(match[:swap_size]) usage.resident_size += resident_size usage.swap_size += swap_size # Private vs. Shared memory: COW=copy_on_write PRV=private NUL=empty ALI=aliased SHM=shared ZER=zero_filled S/A=shared_alias case match[:sharing_mode] when "PRV" usage.private_clean_size += resident_size - dirty_size usage.private_dirty_size += dirty_size when "COW", "SHM" usage.shared_clean_size += resident_size - dirty_size usage.shared_dirty_size += dirty_size end # Anonymous memory: no region detail path or special names if match[:region_name] =~ /MALLOC|VM_ALLOCATE|Stack|STACK|anonymous/ usage.anonymous_size += resident_size end end end # vmmap might not fail, but also might not return any data. return nil if usage.map_count.zero? # Darwin does not expose proportional memory usage, so we guess based on the number of processes. Yes, this is a terrible hack, but it's the most reasonable thing to do given the constraints: usage.proportional_size = usage.resident_size / count usage.proportional_swap_size = usage.swap_size / count return usage end rescue Errno::ESRCH # Process doesn't exist. return nil end |
.parse_size(size_string) ⇒ Object
Parse a size string from vmmap (e.g. "4K", "1.5M", "2G") into bytes.
21 22 23 24 25 26 27 28 29 30 |
# File 'lib/process/metrics/memory/darwin.rb', line 21 def self.parse_size(size_string) return 0 unless size_string case size_string.strip when /([\d\.]+)K/i then ($1.to_f * 1024).round when /([\d\.]+)M/i then ($1.to_f * 1024 * 1024).round when /([\d\.]+)G/i then ($1.to_f * 1024 * 1024 * 1024).round else (size_string.to_f).ceil end end |
.supported? ⇒ Boolean
Whether the memory usage can be captured on this system.
14 15 16 |
# File 'lib/process/metrics/memory/darwin.rb', line 14 def self.supported? File.executable?(VMMAP) end |