class Darwin
Definitions
def self.supported?
Whether the memory usage can be captured on this system.
Implementation
def self.supported?
File.executable?(VMMAP)
end
def self.total_size
Signature
-
returns
Numeric
Total memory size in kilobytes.
Implementation
def self.total_size
# sysctl hw.memsize
IO.popen(["sysctl", "hw.memsize"], "r") do |io|
io.each_line do |line|
if line =~ /hw.memsize: (\d+)/
return $1.to_i / 1024
end
end
end
end
def self.parse_size(string)
Parse a size string into kilobytes.
Implementation
def self.parse_size(string)
return 0 unless string
case string.strip
when /([\d\.]+)K/i then ($1.to_f).round
when /([\d\.]+)M/i then ($1.to_f * 1024).round
when /([\d\.]+)G/i then ($1.to_f * 1024 * 1024).round
else (string.to_f / 1024).ceil
end
end
def self.capture(pid, count: 1, **options)
Capture memory usage for the given process IDs.
Implementation
def self.capture(pid, count: 1, **options)
usage = Memory.zero
IO.popen(["vmmap", pid.to_s], "r") do |io|
io.each_line do |line|
if match = LINE.match(line)
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])
# Update counts
usage.map_count += 1
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
end
# 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