1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
# frozen_string_literal: true
begin
require '-test-/memory_status.so'
rescue LoadError
end
module Memory
keys = []
case
when File.exist?(procfile = "/proc/self/status") && (pat = /^Vm(\w+):\s+(\d+)/) =~ (data = File.binread(procfile))
PROC_FILE = procfile
VM_PAT = pat
def self.read_status
File.foreach(PROC_FILE, encoding: Encoding::ASCII_8BIT) do |l|
yield($1.downcase.intern, $2.to_i * 1024) if VM_PAT =~ l
end
end
data.scan(pat) {|k, v| keys << k.downcase.intern}
when /mswin|mingw/ =~ RUBY_PLATFORM
keys.push(:size, :rss, :peak)
begin
require 'fiddle/import'
require 'fiddle/types'
rescue LoadError
# Fallback to PowerShell command to get memory information for current process
def self.read_status
cmd = [
"powershell.exe", "-NoProfile", "-Command",
"Get-Process -Id #{$$} | " \
"% { Write-Output $_.PagedMemorySize64 $_.WorkingSet64 $_.PeakWorkingSet64 }"
]
IO.popen(cmd, "r", err: [:child, :out]) do |out|
if /^(\d+)\n(\d+)\n(\d+)$/ =~ out.read
yield :size, $1.to_i
yield :rss, $2.to_i
yield :peak, $3.to_i
end
end
end
else
module Win32
extend Fiddle::Importer
dlload "kernel32.dll", "psapi.dll"
include Fiddle::Win32Types
typealias "SIZE_T", "size_t"
PROCESS_MEMORY_COUNTERS = struct [
"DWORD cb",
"DWORD PageFaultCount",
"SIZE_T PeakWorkingSetSize",
"SIZE_T WorkingSetSize",
"SIZE_T QuotaPeakPagedPoolUsage",
"SIZE_T QuotaPagedPoolUsage",
"SIZE_T QuotaPeakNonPagedPoolUsage",
"SIZE_T QuotaNonPagedPoolUsage",
"SIZE_T PagefileUsage",
"SIZE_T PeakPagefileUsage",
]
typealias "PPROCESS_MEMORY_COUNTERS", "PROCESS_MEMORY_COUNTERS*"
extern "HANDLE GetCurrentProcess()", :stdcall
extern "BOOL GetProcessMemoryInfo(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD)", :stdcall
module_function
def memory_info
size = PROCESS_MEMORY_COUNTERS.size
data = PROCESS_MEMORY_COUNTERS.malloc
data.cb = size
data if GetProcessMemoryInfo(GetCurrentProcess(), data, size)
end
end
def self.read_status
if info = Win32.memory_info
yield :size, info.PagefileUsage
yield :rss, info.WorkingSetSize
yield :peak, info.PeakWorkingSetSize
end
end
end
when (require_relative 'find_executable'
pat = /^\s*(\d+)\s+(\d+)$/
pscmd = EnvUtil.find_executable("ps", "-ovsz=", "-orss=", "-p", $$.to_s) {|out| pat =~ out})
pscmd.pop
PAT = pat
PSCMD = pscmd
keys << :size << :rss
def self.read_status
if PAT =~ IO.popen(PSCMD + [$$.to_s], "r", err: [:child, :out], &:read)
yield :size, $1.to_i*1024
yield :rss, $2.to_i*1024
end
end
else
def self.read_status
raise NotImplementedError, "unsupported platform"
end
end
if !keys.empty?
Status = Struct.new(*keys)
end
end unless defined?(Memory::Status)
if defined?(Memory::Status)
class Memory::Status
def _update
Memory.read_status do |key, val|
self[key] = val
end
self
end unless method_defined?(:_update)
Header = members.map {|k| k.to_s.upcase.rjust(6)}.join('')
Format = "%6d"
def initialize
_update
end
def to_s
status = each_pair.map {|n,v|
"#{n}:#{v}"
}
"{#{status.join(",")}}"
end
def self.parse(str)
status = allocate
str.scan(/(?:\A\{|\G,)(#{members.join('|')}):(\d+)(?=,|\}\z)/) do
status[$1] = $2.to_i
end
status
end
end
# On some platforms (e.g. Solaris), libc malloc does not return
# freed memory to OS because of efficiency, and linking with extra
# malloc library is needed to detect memory leaks.
#
case RUBY_PLATFORM
when /solaris2\.(?:9|[1-9][0-9])/i # Solaris 9, 10, 11,...
bits = [nil].pack('p').size == 8 ? 64 : 32
if ENV['LD_PRELOAD'].to_s.empty? &&
ENV["LD_PRELOAD_#{bits}"].to_s.empty? &&
(ENV['UMEM_OPTIONS'].to_s.empty? ||
ENV['UMEM_OPTIONS'] == 'backend=mmap') then
envs = {
'LD_PRELOAD' => 'libumem.so',
'UMEM_OPTIONS' => 'backend=mmap'
}
args = [
envs,
"--disable=gems",
"-v", "-",
]
_, err, status = EnvUtil.invoke_ruby(args, "exit(0)", true, true)
if status.exitstatus == 0 && err.to_s.empty? then
Memory::NO_MEMORY_LEAK_ENVS = envs
end
end
end #case RUBY_PLATFORM
end
|