sys-proctable 0.7.6 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -0
- data/CHANGES +165 -0
- data/MANIFEST +33 -41
- data/README +115 -135
- data/Rakefile +94 -0
- data/benchmarks/bench_ps.rb +21 -0
- data/doc/top.txt +5 -11
- data/examples/example_ps.rb +20 -0
- data/lib/aix/sys/proctable.rb +458 -0
- data/lib/darwin/sys/proctable.rb +363 -0
- data/lib/freebsd/sys/proctable.rb +363 -0
- data/lib/linux/sys/proctable.rb +314 -0
- data/lib/linux/sys/proctable/cgroup_entry.rb +50 -0
- data/lib/linux/sys/proctable/smaps.rb +118 -0
- data/lib/sunos/sys/proctable.rb +456 -0
- data/lib/sys-proctable.rb +1 -0
- data/lib/sys-top.rb +1 -0
- data/lib/sys/proctable.rb +18 -0
- data/lib/sys/proctable/version.rb +6 -0
- data/lib/sys/top.rb +28 -19
- data/lib/windows/sys/proctable.rb +208 -0
- data/spec/sys_proctable_aix_spec.rb +328 -0
- data/spec/sys_proctable_all_spec.rb +89 -0
- data/spec/sys_proctable_darwin_spec.rb +120 -0
- data/spec/sys_proctable_freebsd_spec.rb +210 -0
- data/spec/sys_proctable_linux_spec.rb +310 -0
- data/spec/sys_proctable_sunos_spec.rb +316 -0
- data/spec/sys_proctable_windows_spec.rb +317 -0
- data/spec/sys_top_spec.rb +51 -0
- data/sys-proctable.gemspec +38 -0
- metadata +140 -64
- metadata.gz.sig +0 -0
- data/doc/freebsd.txt +0 -90
- data/doc/hpux.txt +0 -77
- data/doc/linux.txt +0 -85
- data/doc/solaris.txt +0 -99
- data/doc/windows.txt +0 -122
- data/ext/extconf.rb +0 -98
- data/ext/sunos/sunos.c +0 -374
- data/ext/sunos/sunos.h +0 -177
- data/ext/version.h +0 -2
- data/test/tc_all.rb +0 -59
- data/test/tc_freebsd.rb +0 -45
- data/test/tc_hpux.rb +0 -49
- data/test/tc_kvm_bsd.rb +0 -31
- data/test/tc_linux.rb +0 -45
- data/test/tc_sunos.rb +0 -52
- data/test/tc_top.rb +0 -26
- data/test/tc_windows.rb +0 -40
- data/test/test_memleak.rb +0 -54
@@ -0,0 +1,314 @@
|
|
1
|
+
require 'sys/proctable/version'
|
2
|
+
require_relative 'proctable/cgroup_entry'
|
3
|
+
require_relative 'proctable/smaps'
|
4
|
+
|
5
|
+
# The Sys module serves as a namespace only.
|
6
|
+
module Sys
|
7
|
+
|
8
|
+
# The ProcTable class encapsulates process table information.
|
9
|
+
class ProcTable
|
10
|
+
|
11
|
+
# Error typically raised if the ProcTable.ps method fails.
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
# There is no constructor
|
15
|
+
private_class_method :new
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
@mem_total = IO.read("/proc/meminfo")[/MemTotal.*/].split[1].to_i * 1024 rescue nil
|
20
|
+
@boot_time = IO.read("/proc/stat")[/btime.*/].split.last.to_i rescue nil
|
21
|
+
|
22
|
+
@fields = [
|
23
|
+
'cmdline', # Complete command line
|
24
|
+
'cwd', # Current working directory
|
25
|
+
'environ', # Environment
|
26
|
+
'exe', # Actual pathname of the executed command
|
27
|
+
'fd', # File descriptors open by process
|
28
|
+
'root', # Root directory of process
|
29
|
+
'pid', # Process ID
|
30
|
+
'comm', # Filename of executable
|
31
|
+
'state', # Single character state abbreviation
|
32
|
+
'ppid', # Parent process ID
|
33
|
+
'pgrp', # Process group
|
34
|
+
'session', # Session ID
|
35
|
+
'tty_nr', # TTY (terminal) associated with the process
|
36
|
+
'tpgid', # Group ID of the TTY
|
37
|
+
'flags', # Kernel flags
|
38
|
+
'minflt', # Number of minor faults
|
39
|
+
'cminflt', # Number of minor faults of waited-for children
|
40
|
+
'majflt', # Number of major faults
|
41
|
+
'cmajflt', # Number of major faults of waited-for children
|
42
|
+
'utime', # Number of user mode jiffies
|
43
|
+
'stime', # Number of kernel mode jiffies
|
44
|
+
'cutime', # Number of children's user mode jiffies
|
45
|
+
'cstime', # Number of children's kernel mode jiffies
|
46
|
+
'priority', # Nice value plus 15
|
47
|
+
'nice', # Nice value
|
48
|
+
'itrealvalue', # Time in jiffies before next SIGALRM
|
49
|
+
'starttime', # Time in jiffies since system boot
|
50
|
+
'vsize', # Virtual memory size in bytes
|
51
|
+
'rss', # Resident set size
|
52
|
+
'rlim', # Current limit on the rss in bytes
|
53
|
+
'startcode', # Address above which program text can run
|
54
|
+
'endcode', # Address below which program text can run
|
55
|
+
'startstack', # Address of the startstack
|
56
|
+
'kstkesp', # Kernel stack page address
|
57
|
+
'kstkeip', # Kernel instruction pointer
|
58
|
+
'signal', # Bitmap of pending signals
|
59
|
+
'blocked', # Bitmap of blocked signals
|
60
|
+
'sigignore', # Bitmap of ignored signals
|
61
|
+
'sigcatch', # Bitmap of caught signals
|
62
|
+
'wchan', # Channel in which the process is waiting
|
63
|
+
'nswap', # Number of pages swapped
|
64
|
+
'cnswap', # Cumulative nswap for child processes
|
65
|
+
'exit_signal', # Signal to be sent to parent when process dies
|
66
|
+
'processor', # CPU number last executed on
|
67
|
+
'rt_priority', # Real time scheduling priority
|
68
|
+
'policy', # Scheduling policy
|
69
|
+
'name', # Process name
|
70
|
+
'uid', # Real user ID
|
71
|
+
'euid', # Effective user ID
|
72
|
+
'gid', # Real group ID
|
73
|
+
'egid', # Effective group ID
|
74
|
+
'pctcpu', # Percent of CPU usage (custom field)
|
75
|
+
'pctmem', # Percent of Memory usage (custom field)
|
76
|
+
'nlwp', # Number of Light-Weight Processes associated with the process (threads)
|
77
|
+
'cgroup', # Control groups to which the process belongs
|
78
|
+
'smaps' # Process memory size for all mapped files
|
79
|
+
]
|
80
|
+
|
81
|
+
public
|
82
|
+
|
83
|
+
ProcTableStruct = Struct.new('ProcTableStruct', *@fields)
|
84
|
+
|
85
|
+
# In block form, yields a ProcTableStruct for each process entry that you
|
86
|
+
# have rights to. This method returns an array of ProcTableStruct's in
|
87
|
+
# non-block form.
|
88
|
+
#
|
89
|
+
# If a +pid+ is provided, then only a single ProcTableStruct is yielded or
|
90
|
+
# returned, or nil if no process information is found for that +pid+.
|
91
|
+
#
|
92
|
+
# Example:
|
93
|
+
#
|
94
|
+
# # Iterate over all processes
|
95
|
+
# ProcTable.ps do |proc_info|
|
96
|
+
# p proc_info
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# # Print process table information for only pid 1001
|
100
|
+
# p ProcTable.ps(pid: 1001)
|
101
|
+
#
|
102
|
+
# # Skip smaps collection and/or cgroup collection
|
103
|
+
# p ProcTable.ps(smaps: false, cgroup: false)
|
104
|
+
#
|
105
|
+
#--
|
106
|
+
# It's possible that a process could terminate while gathering
|
107
|
+
# information for that process. When that happens, this library
|
108
|
+
# will simply skip to the next record. In short, this library will
|
109
|
+
# either return all information for a process, or none at all.
|
110
|
+
#
|
111
|
+
def self.ps(**kwargs)
|
112
|
+
pid = kwargs[:pid]
|
113
|
+
smaps = kwargs[:smaps]
|
114
|
+
cgroup = kwargs[:cgroup]
|
115
|
+
|
116
|
+
array = block_given? ? nil : []
|
117
|
+
struct = nil
|
118
|
+
|
119
|
+
raise TypeError unless pid.is_a?(Numeric) if pid
|
120
|
+
|
121
|
+
Dir.foreach("/proc"){ |file|
|
122
|
+
next if file =~ /\D/ # Skip non-numeric directories
|
123
|
+
next unless file.to_i == pid if pid
|
124
|
+
|
125
|
+
struct = ProcTableStruct.new
|
126
|
+
|
127
|
+
# Get /proc/<pid>/cmdline information. Strip out embedded nulls.
|
128
|
+
begin
|
129
|
+
data = IO.read("/proc/#{file}/cmdline").tr("\000", ' ').strip
|
130
|
+
struct.cmdline = data
|
131
|
+
rescue
|
132
|
+
next # Process terminated, on to the next process
|
133
|
+
end
|
134
|
+
|
135
|
+
# Get /proc/<pid>/cwd information
|
136
|
+
struct.cwd = File.readlink("/proc/#{file}/cwd") rescue nil
|
137
|
+
|
138
|
+
# Get /proc/<pid>/environ information. Environment information
|
139
|
+
# is represented as a Hash, with the environment variable as the
|
140
|
+
# key and its value as the hash value.
|
141
|
+
struct.environ = {}
|
142
|
+
|
143
|
+
begin
|
144
|
+
IO.read("/proc/#{file}/environ").split("\0").each{ |str|
|
145
|
+
key, value = str.split('=')
|
146
|
+
struct.environ[key] = value
|
147
|
+
}
|
148
|
+
rescue Errno::EACCES, Errno::ESRCH, Errno::ENOENT
|
149
|
+
# Ignore and move on.
|
150
|
+
end
|
151
|
+
|
152
|
+
# Get /proc/<pid>/exe information
|
153
|
+
struct.exe = File.readlink("/proc/#{file}/exe") rescue nil
|
154
|
+
|
155
|
+
# Get /proc/<pid>/fd information. File descriptor information
|
156
|
+
# is represented as a Hash, with the fd as the key, and its
|
157
|
+
# symlink as the value.
|
158
|
+
struct.fd = {}
|
159
|
+
|
160
|
+
begin
|
161
|
+
Dir["/proc/#{file}/fd/*"].each do |fd|
|
162
|
+
struct.fd[File.basename(fd)] = File.readlink(fd) rescue nil
|
163
|
+
end
|
164
|
+
rescue
|
165
|
+
# Ignore and move on
|
166
|
+
end
|
167
|
+
|
168
|
+
# Get /proc/<pid>/root information
|
169
|
+
struct.root = File.readlink("/proc/#{file}/root") rescue nil
|
170
|
+
|
171
|
+
# Get /proc/<pid>/stat information
|
172
|
+
stat = IO.read("/proc/#{file}/stat") rescue next
|
173
|
+
|
174
|
+
# Get number of LWP, one directory for each in /proc/<pid>/task/
|
175
|
+
# Every process has at least one thread, so if we fail to read the task directory, set nlwp to 1.
|
176
|
+
struct.nlwp = Dir.glob("/proc/#{file}/task/*").length rescue struct.nlwp = 1
|
177
|
+
|
178
|
+
# Get control groups to which the process belongs
|
179
|
+
unless cgroup == false
|
180
|
+
struct.cgroup = IO.readlines("/proc/#{file}/cgroup").map { |l| CgroupEntry.new(l) } rescue []
|
181
|
+
end
|
182
|
+
|
183
|
+
# Read smaps, returning a parsable string if we don't have permissions.
|
184
|
+
# Note: We're blindly rescuing because File.readable?/readable_real?
|
185
|
+
# are true for a file in the /proc fileystem but raises a Errno:EACCESS
|
186
|
+
# when your try to read it without permissions.
|
187
|
+
unless smaps == false
|
188
|
+
smaps_contents = IO.read("/proc/#{file}/smaps") rescue ""
|
189
|
+
struct.smaps = Smaps.new(file, smaps_contents)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Deal with spaces in comm name. Courtesy of Ara Howard.
|
193
|
+
re = %r/\([^\)]+\)/
|
194
|
+
comm = stat[re]
|
195
|
+
comm.tr!(' ', '-')
|
196
|
+
stat[re] = comm
|
197
|
+
|
198
|
+
stat = stat.split
|
199
|
+
|
200
|
+
struct.pid = stat[0].to_i
|
201
|
+
struct.comm = stat[1].tr('()','') # Remove parens
|
202
|
+
struct.state = stat[2]
|
203
|
+
struct.ppid = stat[3].to_i
|
204
|
+
struct.pgrp = stat[4].to_i
|
205
|
+
struct.session = stat[5].to_i
|
206
|
+
struct.tty_nr = stat[6].to_i
|
207
|
+
struct.tpgid = stat[7].to_i
|
208
|
+
struct.flags = stat[8].to_i
|
209
|
+
struct.minflt = stat[9].to_i
|
210
|
+
struct.cminflt = stat[10].to_i
|
211
|
+
struct.majflt = stat[11].to_i
|
212
|
+
struct.cmajflt = stat[12].to_i
|
213
|
+
struct.utime = stat[13].to_i
|
214
|
+
struct.stime = stat[14].to_i
|
215
|
+
struct.cutime = stat[15].to_i
|
216
|
+
struct.cstime = stat[16].to_i
|
217
|
+
struct.priority = stat[17].to_i
|
218
|
+
struct.nice = stat[18].to_i
|
219
|
+
# Skip 19
|
220
|
+
struct.itrealvalue = stat[20].to_i
|
221
|
+
struct.starttime = stat[21].to_i
|
222
|
+
struct.vsize = stat[22].to_i
|
223
|
+
struct.rss = stat[23].to_i
|
224
|
+
struct.rlim = stat[24].to_i
|
225
|
+
struct.startcode = stat[25].to_i
|
226
|
+
struct.endcode = stat[26].to_i
|
227
|
+
struct.startstack = stat[27].to_i
|
228
|
+
struct.kstkesp = stat[28].to_i
|
229
|
+
struct.kstkeip = stat[29].to_i
|
230
|
+
struct.signal = stat[30].to_i
|
231
|
+
struct.blocked = stat[31].to_i
|
232
|
+
struct.sigignore = stat[32].to_i
|
233
|
+
struct.sigcatch = stat[33].to_i
|
234
|
+
struct.wchan = stat[34].to_i
|
235
|
+
struct.nswap = stat[35].to_i
|
236
|
+
struct.cnswap = stat[36].to_i
|
237
|
+
struct.exit_signal = stat[37].to_i
|
238
|
+
struct.processor = stat[38].to_i
|
239
|
+
struct.rt_priority = stat[39].to_i
|
240
|
+
struct.policy = stat[40].to_i
|
241
|
+
|
242
|
+
# Get /proc/<pid>/status information (name, uid, euid, gid, egid)
|
243
|
+
begin
|
244
|
+
IO.foreach("/proc/#{file}/status") do |line|
|
245
|
+
case line
|
246
|
+
when /Name:\s*?(\w+)/
|
247
|
+
struct.name = $1
|
248
|
+
when /Uid:\s*?(\d+)\s*?(\d+)/
|
249
|
+
struct.uid = $1.to_i
|
250
|
+
struct.euid = $2.to_i
|
251
|
+
when /Gid:\s*?(\d+)\s*?(\d+)/
|
252
|
+
struct.gid = $1.to_i
|
253
|
+
struct.egid = $2.to_i
|
254
|
+
end
|
255
|
+
end
|
256
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
257
|
+
next
|
258
|
+
end
|
259
|
+
|
260
|
+
# If cmdline is empty use comm instead
|
261
|
+
struct.cmdline = struct.comm if struct.cmdline.empty?
|
262
|
+
|
263
|
+
# Manually calculate CPU and memory usage
|
264
|
+
struct.pctcpu = get_pctcpu(struct.utime, struct.starttime)
|
265
|
+
struct.pctmem = get_pctmem(struct.rss)
|
266
|
+
|
267
|
+
struct.freeze # This is read-only data
|
268
|
+
|
269
|
+
if block_given?
|
270
|
+
yield struct
|
271
|
+
else
|
272
|
+
array << struct
|
273
|
+
end
|
274
|
+
}
|
275
|
+
|
276
|
+
pid ? struct : array
|
277
|
+
end
|
278
|
+
|
279
|
+
# Returns an array of fields that each ProcTableStruct will contain. This
|
280
|
+
# may be useful if you want to know in advance what fields are available
|
281
|
+
# without having to perform at least one read of the /proc table.
|
282
|
+
#
|
283
|
+
# Example:
|
284
|
+
#
|
285
|
+
# Sys::ProcTable.fields.each{ |field|
|
286
|
+
# puts "Field: #{field}"
|
287
|
+
# }
|
288
|
+
#
|
289
|
+
def self.fields
|
290
|
+
@fields
|
291
|
+
end
|
292
|
+
|
293
|
+
private
|
294
|
+
|
295
|
+
# Calculate the percentage of memory usage for the given process.
|
296
|
+
#
|
297
|
+
def self.get_pctmem(rss)
|
298
|
+
return nil unless @mem_total
|
299
|
+
page_size = 4096
|
300
|
+
rss_total = rss * page_size
|
301
|
+
sprintf("%3.2f", (rss_total.to_f / @mem_total) * 100).to_f
|
302
|
+
end
|
303
|
+
|
304
|
+
# Calculate the percentage of CPU usage for the given process.
|
305
|
+
#
|
306
|
+
def self.get_pctcpu(utime, start_time)
|
307
|
+
return nil unless @boot_time
|
308
|
+
hertz = 100.0
|
309
|
+
utime = (utime * 10000).to_f
|
310
|
+
stime = (start_time.to_f / hertz) + @boot_time
|
311
|
+
sprintf("%3.2f", (utime / 10000.0) / (Time.now.to_i - stime)).to_f
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Sys
|
2
|
+
class ProcTable
|
3
|
+
# This represents a cgroup entry
|
4
|
+
#
|
5
|
+
# Have a look at `man 5 proc` on a linux distribution, to get some more
|
6
|
+
# information about the lines and their fields in `/proc/[pid]/cgroup`.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# entry = CgroupEntry.new '7:devices:/init.scope'
|
11
|
+
# entry.hierarchy_id # => 7
|
12
|
+
# entry.subsystems # => ['devices']
|
13
|
+
# entry.control_group # => '/init.scope'
|
14
|
+
#
|
15
|
+
class CgroupEntry
|
16
|
+
# Create a new cgroup entry object
|
17
|
+
#
|
18
|
+
# This expects a string of '7:devices:/init.scope' - see `man 5 proc` for a
|
19
|
+
# reference.
|
20
|
+
def initialize(string)
|
21
|
+
@string = string.chomp
|
22
|
+
@fields = @string.split(/:/)
|
23
|
+
rescue
|
24
|
+
@fields = []
|
25
|
+
end
|
26
|
+
|
27
|
+
# This returns the hierarchy id of the cgroup entry
|
28
|
+
def hierarchy_id
|
29
|
+
@fields[0].to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return sets of subsystems bound to the hierarchy
|
33
|
+
def subsystems
|
34
|
+
@fields[1].split(/,/)
|
35
|
+
rescue
|
36
|
+
[]
|
37
|
+
end
|
38
|
+
|
39
|
+
# control group in the hierarchy to which the process belongs
|
40
|
+
def control_group
|
41
|
+
@fields[2]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the line itself
|
45
|
+
def to_s
|
46
|
+
@string
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Sys
|
2
|
+
class ProcTable
|
3
|
+
# Smaps represents a process' memory size for all mapped files
|
4
|
+
#
|
5
|
+
# A single mapped file memory entry looks like this:
|
6
|
+
#
|
7
|
+
# 00400000-004d4000 r-xp 00000000 fd:00 785 /bin/bash
|
8
|
+
# Size: 848 kB
|
9
|
+
# Rss: 572 kB
|
10
|
+
# Pss: 572 kB
|
11
|
+
# Shared_Clean: 0 kB
|
12
|
+
# Shared_Dirty: 0 kB
|
13
|
+
# Private_Clean: 572 kB
|
14
|
+
# Private_Dirty: 0 kB
|
15
|
+
# Referenced: 572 kB
|
16
|
+
# Anonymous: 0 kB
|
17
|
+
# AnonHugePages: 0 kB
|
18
|
+
# Swap: 0 kB
|
19
|
+
# KernelPageSize: 4 kB
|
20
|
+
# MMUPageSize: 4 kB
|
21
|
+
#
|
22
|
+
# Have a look at `man 5 proc` on a linux distribution, to get some more
|
23
|
+
# information about the lines and fields in `/proc/[pid]/smaps`.
|
24
|
+
#
|
25
|
+
# Example:
|
26
|
+
#
|
27
|
+
# smaps = Smaps.new(123, IO.read("/proc/1234/smaps")
|
28
|
+
# => #<Sys::ProcTable::Smaps:0x007f8ac5930768 @pid=123, @pss=107000, @rss=368000, @uss=96000, @swap=192000, @vss=136752000>
|
29
|
+
# smaps.pss # => 109568
|
30
|
+
# smaps.rss # => 376832
|
31
|
+
# smaps.uss # => 98304
|
32
|
+
# smaps.swap # => 196608
|
33
|
+
# smaps.vss # => 140034048
|
34
|
+
#
|
35
|
+
class Smaps
|
36
|
+
|
37
|
+
# Process ID for this smaps
|
38
|
+
attr_reader :pid
|
39
|
+
|
40
|
+
# Proportional set size
|
41
|
+
#
|
42
|
+
# PSS is the size of private pages added to each shared mapping's size
|
43
|
+
# divided by the number of processes that share it. It is meant to
|
44
|
+
# provide a better representation of the amount of memory actually used
|
45
|
+
# by a process.
|
46
|
+
#
|
47
|
+
# If a process has 4k of private pages, 4k of shared pages shared with one
|
48
|
+
# other process, and 3k of pages shared with two other processes, the PSS
|
49
|
+
# is:
|
50
|
+
#
|
51
|
+
# 4k + (4k / 2) + (3k / 3) = 7k
|
52
|
+
#
|
53
|
+
attr_reader :pss
|
54
|
+
alias_method :proportional_set_size, :pss
|
55
|
+
|
56
|
+
# Resident set size
|
57
|
+
#
|
58
|
+
# RSS is the total size of all pages, shared or not, mapped to a process.
|
59
|
+
attr_reader :rss
|
60
|
+
alias_method :resident_set_size, :rss
|
61
|
+
|
62
|
+
# Unique set size
|
63
|
+
#
|
64
|
+
# USS is the total size of all private pages mapped to a process.
|
65
|
+
attr_reader :uss
|
66
|
+
alias_method :unique_set_size, :uss
|
67
|
+
|
68
|
+
# Swap
|
69
|
+
#
|
70
|
+
# Swap is the total size of all swapped pages mapped to a process.
|
71
|
+
attr_reader :swap
|
72
|
+
|
73
|
+
# Virtual set size
|
74
|
+
#
|
75
|
+
# VSS is the total accessible address space in a process. Since files are
|
76
|
+
# lazily loaded, this value represents the total size of all mapped files
|
77
|
+
# if they were all loaded.
|
78
|
+
attr_reader :vss
|
79
|
+
alias_method :virtual_set_size, :vss
|
80
|
+
|
81
|
+
# Create a new smaps object
|
82
|
+
#
|
83
|
+
#
|
84
|
+
# This expects a process id and a string containing the contents of
|
85
|
+
# /proc/PID/smaps - see `man 5 proc` for a reference.
|
86
|
+
#
|
87
|
+
# The smaps contents are parsed and memory sizes are calculated in bytes.
|
88
|
+
def initialize(pid, smaps_contents)
|
89
|
+
@pid = pid
|
90
|
+
@pss = 0
|
91
|
+
@rss = 0
|
92
|
+
@uss = 0
|
93
|
+
@swap = 0
|
94
|
+
@vss = 0
|
95
|
+
smaps_contents.each_line { |line| parse_smaps_line(line) }
|
96
|
+
end
|
97
|
+
|
98
|
+
alias_method :to_s, :inspect
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def parse_smaps_line(line)
|
103
|
+
case line
|
104
|
+
when /^Pss:\s+?(\d+)/
|
105
|
+
@pss += Regexp.last_match[1].to_i * 1000
|
106
|
+
when /^Rss:\s+?(\d+)/
|
107
|
+
@rss += Regexp.last_match[1].to_i * 1000
|
108
|
+
when /^Size:\s+?(\d+)/
|
109
|
+
@vss += Regexp.last_match[1].to_i * 1000
|
110
|
+
when /^Swap:\s+?(\d+)/
|
111
|
+
@swap += Regexp.last_match[1].to_i * 1000
|
112
|
+
when /^Private_(Clean|Dirty):\s+?(\d+)/
|
113
|
+
@uss += Regexp.last_match[2].to_i * 1000
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|