-
Notifications
You must be signed in to change notification settings - Fork 31
/
get_process_mem.rb
124 lines (106 loc) · 3.28 KB
/
get_process_mem.rb
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
require 'pathname'
require 'bigdecimal'
# Cribbed from Unicorn Worker Killer, thanks!
class GetProcessMem
private_class_method def self.number_to_bigdecimal(value)
BigDecimal(value)
end
private def number_to_bigdecimal(value)
self.class.send(:number_to_bigdecimal, value)
end
KB_TO_BYTE = number_to_bigdecimal 1024 # 2**10 = 1024
MB_TO_BYTE = number_to_bigdecimal 1_048_576 # 1024**2 = 1_048_576
GB_TO_BYTE = number_to_bigdecimal 1_073_741_824 # 1024**3 = 1_073_741_824
CONVERSION = { "kb" => KB_TO_BYTE, "mb" => MB_TO_BYTE, "gb" => GB_TO_BYTE }
ROUND_UP = number_to_bigdecimal "0.5"
attr_reader :pid
RUNS_ON_WINDOWS = Gem.win_platform?
if RUNS_ON_WINDOWS
begin
require 'sys/proctable'
rescue LoadError => e
message = "Please add `sys-proctable` to your Gemfile for windows machines\n"
message << e.message
raise e, message
end
include Sys
end
RUNS_ON_DARWIN = Gem.platforms.detect do |p|
p.is_a?(Gem::Platform) && p.os == 'darwin'
end
if RUNS_ON_DARWIN
begin
require 'get_process_mem/darwin'
rescue LoadError => e
message = "Please add `ffi` to your Gemfile for darwin (macos) machines\n"
message << e.message
raise e, message
end
end
def initialize(pid = Process.pid)
@status_file = Pathname.new "/proc/#{pid}/status"
@process_file = Pathname.new "/proc/#{pid}/smaps"
@pid = pid
@linux = @status_file.exist?
end
def linux?
@linux
end
def bytes
memory = linux_status_memory if linux?
memory ||= darwin_memory if RUNS_ON_DARWIN && Process.pid == pid
memory ||= ps_memory
end
def kb(b = bytes)
(b/KB_TO_BYTE).to_f
end
def mb(b = bytes)
(b/MB_TO_BYTE).to_f
end
def gb(b = bytes)
(b/GB_TO_BYTE).to_f
end
def inspect
b = bytes
"#<#{self.class}:0x%08x @mb=#{ mb b } @gb=#{ gb b } @kb=#{ kb b } @bytes=#{b}>" % (object_id * 2)
end
# linux stores memory info in a file "/proc/#{pid}/status"
# If it's available it uses less resources than shelling out to ps
def linux_status_memory(file = @status_file)
line = file.each_line.detect {|line| line.start_with? 'VmRSS'.freeze }
return unless line
return unless (_name, value, unit = line.split(nil)).length == 3
CONVERSION[unit.downcase!] * value.to_i
rescue Errno::EACCES, Errno::ENOENT
0
end
# linux stores detailed memory info in a file "/proc/#{pid}/smaps"
def linux_memory(file = @process_file)
lines = file.each_line.select {|line| line.match(/^Rss/) }
return if lines.empty?
lines.reduce(0) do |sum, line|
line.match(/(?<value>(\d*\.{0,1}\d+))\s+(?<unit>\w\w)/) do |m|
value = number_to_bigdecimal(m[:value]) + ROUND_UP
unit = m[:unit].downcase
sum += CONVERSION[unit] * value
end
sum
end
rescue Errno::EACCES
0
end
# Pull memory from `ps` command, takes more resources and can freeze
# in low memory situations
def ps_memory
if RUNS_ON_WINDOWS
size = ProcTable.ps(pid: pid).working_set_size
number_to_bigdecimal(size)
else
mem = `ps -o rss= -p #{pid}`
KB_TO_BYTE * number_to_bigdecimal(mem == "" ? 0 : mem)
end
end
def darwin_memory
Darwin.resident_size
end
end