sys-proctable 1.2.0 → 1.2.5
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -1
- data/{CHANGES → CHANGES.rdoc} +37 -0
- data/LICENSE +177 -0
- data/{MANIFEST → MANIFEST.rdoc} +3 -10
- data/README.md +158 -0
- data/Rakefile +1 -1
- data/benchmarks/bench_ips_ps.rb +63 -0
- data/lib/darwin/sys/proctable.rb +78 -35
- data/lib/linux/sys/proctable.rb +106 -97
- data/lib/sys/proctable/version.rb +1 -1
- data/spec/spec_helper.rb +22 -0
- data/spec/sys_proctable_all_spec.rb +2 -1
- data/spec/sys_proctable_darwin_spec.rb +5 -5
- data/spec/sys_proctable_linux_spec.rb +38 -9
- data/sys-proctable.gemspec +16 -6
- metadata +41 -34
- metadata.gz.sig +0 -0
- data/README +0 -120
- data/doc/top.txt +0 -47
data/lib/darwin/sys/proctable.rb
CHANGED
|
@@ -26,6 +26,11 @@ module Sys
|
|
|
26
26
|
MAXTHREADNAMESIZE = 64
|
|
27
27
|
PROC_PIDPATHINFO_MAXSIZE = MAXPATHLEN * 4
|
|
28
28
|
|
|
29
|
+
# JRuby on Mac
|
|
30
|
+
unless defined? FFI::StructLayout::CharArray
|
|
31
|
+
FFI::StructLayout::CharArray = FFI::StructLayout::CharArrayProxy
|
|
32
|
+
end
|
|
33
|
+
|
|
29
34
|
class ProcBsdInfo < FFI::Struct
|
|
30
35
|
layout(
|
|
31
36
|
:pbi_flags, :uint32_t,
|
|
@@ -174,66 +179,98 @@ module Sys
|
|
|
174
179
|
#
|
|
175
180
|
def self.ps(**kwargs)
|
|
176
181
|
pid = kwargs[:pid]
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
num = proc_listallpids(nil, 0)
|
|
180
|
-
ptr = FFI::MemoryPointer.new(:pid_t, num)
|
|
181
|
-
num = proc_listallpids(ptr, ptr.size)
|
|
182
|
-
|
|
183
|
-
raise SystemCallError.new('proc_listallpids', FFI.errno) if num == 0
|
|
184
|
-
|
|
185
|
-
pids = ptr.get_array_of_int32(0, num).sort
|
|
186
|
-
array = block_given? ? nil : []
|
|
182
|
+
thread_info = kwargs[:thread_info]
|
|
187
183
|
|
|
188
|
-
|
|
189
|
-
|
|
184
|
+
if pid
|
|
185
|
+
raise TypeError unless pid.is_a?(Numeric)
|
|
190
186
|
info = ProcTaskAllInfo.new
|
|
191
187
|
|
|
192
|
-
nb = proc_pidinfo(
|
|
188
|
+
nb = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, info, info.size)
|
|
193
189
|
|
|
194
190
|
if nb <= 0
|
|
195
191
|
if [Errno::EPERM::Errno, Errno::ESRCH::Errno].include?(FFI.errno)
|
|
196
|
-
|
|
192
|
+
return # Either we don't have permission, or the pid no longer exists
|
|
197
193
|
else
|
|
198
194
|
raise SystemCallError.new('proc_pidinfo', FFI.errno)
|
|
199
195
|
end
|
|
200
196
|
end
|
|
201
197
|
|
|
202
|
-
|
|
203
|
-
next if nb != info.size
|
|
198
|
+
return nil if nb != info.size # Invalid data
|
|
204
199
|
|
|
205
200
|
struct = ProcTableStruct.new
|
|
206
201
|
|
|
207
202
|
# Pass by reference
|
|
208
|
-
get_cmd_args_and_env(
|
|
209
|
-
get_thread_info(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
203
|
+
get_cmd_args_and_env(pid, struct)
|
|
204
|
+
get_thread_info(pid, struct, info[:ptinfo]) unless thread_info == false
|
|
205
|
+
apply_info_to_struct(info, struct)
|
|
206
|
+
|
|
207
|
+
struct.freeze
|
|
208
|
+
yield struct if block_given?
|
|
209
|
+
struct
|
|
210
|
+
else
|
|
211
|
+
num = proc_listallpids(nil, 0)
|
|
212
|
+
ptr = FFI::MemoryPointer.new(:pid_t, num)
|
|
213
|
+
num = proc_listallpids(ptr, ptr.size)
|
|
214
|
+
|
|
215
|
+
raise SystemCallError.new('proc_listallpids', FFI.errno) if num == 0
|
|
216
|
+
|
|
217
|
+
pids = ptr.get_array_of_int32(0, num).sort
|
|
218
|
+
array = block_given? ? nil : []
|
|
219
|
+
|
|
220
|
+
pids.each do |lpid|
|
|
221
|
+
next unless pid == lpid if pid
|
|
222
|
+
info = ProcTaskAllInfo.new
|
|
223
|
+
|
|
224
|
+
nb = proc_pidinfo(lpid, PROC_PIDTASKALLINFO, 0, info, info.size)
|
|
225
|
+
|
|
226
|
+
if nb <= 0
|
|
227
|
+
if [Errno::EPERM::Errno, Errno::ESRCH::Errno].include?(FFI.errno)
|
|
228
|
+
next # Either we don't have permission, or the pid no longer exists
|
|
216
229
|
else
|
|
217
|
-
|
|
230
|
+
raise SystemCallError.new('proc_pidinfo', FFI.errno)
|
|
218
231
|
end
|
|
219
232
|
end
|
|
220
|
-
end
|
|
221
233
|
|
|
222
|
-
|
|
234
|
+
# Avoid potentially invalid data
|
|
235
|
+
next if nb != info.size
|
|
223
236
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
237
|
+
struct = ProcTableStruct.new
|
|
238
|
+
|
|
239
|
+
# Pass by reference
|
|
240
|
+
get_cmd_args_and_env(lpid, struct)
|
|
241
|
+
get_thread_info(lpid, struct, info[:ptinfo]) unless thread_info == false
|
|
242
|
+
apply_info_to_struct(info, struct)
|
|
243
|
+
|
|
244
|
+
struct.freeze
|
|
245
|
+
|
|
246
|
+
if block_given?
|
|
247
|
+
yield struct
|
|
248
|
+
else
|
|
249
|
+
array << struct
|
|
250
|
+
end
|
|
228
251
|
end
|
|
229
|
-
end
|
|
230
252
|
|
|
231
|
-
|
|
232
|
-
|
|
253
|
+
array
|
|
254
|
+
end
|
|
233
255
|
end
|
|
234
256
|
|
|
235
257
|
private
|
|
236
258
|
|
|
259
|
+
# Pass by reference method that updates the Ruby struct based on the FFI struct.
|
|
260
|
+
#
|
|
261
|
+
def self.apply_info_to_struct(info, struct)
|
|
262
|
+
# Chop the leading xx_ from the FFI struct members for our ruby struct.
|
|
263
|
+
info.members.each do |nested|
|
|
264
|
+
info[nested].members.each do |member|
|
|
265
|
+
if info[nested][member].is_a?(FFI::StructLayout::CharArray)
|
|
266
|
+
struct[PROC_STRUCT_FIELD_MAP[member]] = info[nested][member].to_s
|
|
267
|
+
else
|
|
268
|
+
struct[PROC_STRUCT_FIELD_MAP[member]] = info[nested][member]
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
237
274
|
# Returns an array of ThreadInfo objects for the given pid.
|
|
238
275
|
#
|
|
239
276
|
def self.get_thread_info(pid, struct, ptinfo)
|
|
@@ -253,7 +290,13 @@ module Sys
|
|
|
253
290
|
|
|
254
291
|
0.upto(max-1) do |index|
|
|
255
292
|
tinfo = ProcThreadInfo.new
|
|
256
|
-
|
|
293
|
+
|
|
294
|
+
# Use read_array_of_uint64 for compatibility with JRuby if necessary.
|
|
295
|
+
if buf[index].respond_to?(:read_uint64)
|
|
296
|
+
nb = proc_pidinfo(pid, PROC_PIDTHREADINFO, buf[index].read_uint64, tinfo, tinfo.size)
|
|
297
|
+
else
|
|
298
|
+
nb = proc_pidinfo(pid, PROC_PIDTHREADINFO, buf[index].read_array_of_uint64(1).first, tinfo, tinfo.size)
|
|
299
|
+
end
|
|
257
300
|
|
|
258
301
|
if nb <= 0
|
|
259
302
|
if [Errno::EPERM::Errno, Errno::ESRCH::Errno].include?(FFI.errno)
|
data/lib/linux/sys/proctable.rb
CHANGED
|
@@ -20,62 +20,67 @@ module Sys
|
|
|
20
20
|
@boot_time = IO.read("/proc/stat")[/btime.*/].split.last.to_i rescue nil
|
|
21
21
|
|
|
22
22
|
@fields = [
|
|
23
|
-
'cmdline',
|
|
24
|
-
'cwd',
|
|
25
|
-
'environ',
|
|
26
|
-
'exe',
|
|
27
|
-
'fd',
|
|
28
|
-
'root',
|
|
29
|
-
'pid',
|
|
30
|
-
'comm',
|
|
31
|
-
'state',
|
|
32
|
-
'ppid',
|
|
33
|
-
'pgrp',
|
|
34
|
-
'session',
|
|
35
|
-
'tty_nr',
|
|
36
|
-
'tpgid',
|
|
37
|
-
'flags',
|
|
38
|
-
'minflt',
|
|
39
|
-
'cminflt',
|
|
40
|
-
'majflt',
|
|
41
|
-
'cmajflt',
|
|
42
|
-
'utime',
|
|
43
|
-
'stime',
|
|
44
|
-
'cutime',
|
|
45
|
-
'cstime',
|
|
46
|
-
'priority',
|
|
47
|
-
'nice',
|
|
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
|
-
'
|
|
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
|
+
'num_threads', # Number of threads in this process
|
|
49
|
+
'itrealvalue', # Time in jiffies before next SIGALRM
|
|
50
|
+
'starttime', # Time in jiffies since system boot
|
|
51
|
+
'vsize', # Virtual memory size in bytes
|
|
52
|
+
'rss', # Resident set size
|
|
53
|
+
'rlim', # Current limit on the rss in bytes (old)
|
|
54
|
+
'rsslim', # Current limit on the rss in bytes (current)
|
|
55
|
+
'startcode', # Address above which program text can run
|
|
56
|
+
'endcode', # Address below which program text can run
|
|
57
|
+
'startstack', # Address of the startstack
|
|
58
|
+
'kstkesp', # Kernel stack page address
|
|
59
|
+
'kstkeip', # Kernel instruction pointer
|
|
60
|
+
'signal', # Bitmap of pending signals
|
|
61
|
+
'blocked', # Bitmap of blocked signals
|
|
62
|
+
'sigignore', # Bitmap of ignored signals
|
|
63
|
+
'sigcatch', # Bitmap of caught signals
|
|
64
|
+
'wchan', # Channel in which the process is waiting
|
|
65
|
+
'nswap', # Number of pages swapped
|
|
66
|
+
'cnswap', # Cumulative nswap for child processes
|
|
67
|
+
'exit_signal', # Signal to be sent to parent when process dies
|
|
68
|
+
'processor', # CPU number last executed on
|
|
69
|
+
'rt_priority', # Real time scheduling priority
|
|
70
|
+
'policy', # Scheduling policy
|
|
71
|
+
'delayacct_blkio_ticks', # Aggregated block I/O delays
|
|
72
|
+
'guest_time', # Guest time of the process
|
|
73
|
+
'cguest_time', # Guest time of the process's children
|
|
74
|
+
'name', # Process name
|
|
75
|
+
'uid', # Real user ID
|
|
76
|
+
'euid', # Effective user ID
|
|
77
|
+
'gid', # Real group ID
|
|
78
|
+
'egid', # Effective group ID
|
|
79
|
+
'pctcpu', # Percent of CPU usage (custom field)
|
|
80
|
+
'pctmem', # Percent of Memory usage (custom field)
|
|
81
|
+
'nlwp', # Number of Light-Weight Processes associated with the process (threads)
|
|
82
|
+
'cgroup', # Control groups to which the process belongs
|
|
83
|
+
'smaps' # Process memory size for all mapped files
|
|
79
84
|
]
|
|
80
85
|
|
|
81
86
|
public
|
|
@@ -197,47 +202,51 @@ module Sys
|
|
|
197
202
|
|
|
198
203
|
stat = stat.split
|
|
199
204
|
|
|
200
|
-
struct.pid
|
|
201
|
-
struct.comm
|
|
202
|
-
struct.state
|
|
203
|
-
struct.ppid
|
|
204
|
-
struct.pgrp
|
|
205
|
-
struct.session
|
|
206
|
-
struct.tty_nr
|
|
207
|
-
struct.tpgid
|
|
208
|
-
struct.flags
|
|
209
|
-
struct.minflt
|
|
210
|
-
struct.cminflt
|
|
211
|
-
struct.majflt
|
|
212
|
-
struct.cmajflt
|
|
213
|
-
struct.utime
|
|
214
|
-
struct.stime
|
|
215
|
-
struct.cutime
|
|
216
|
-
struct.cstime
|
|
217
|
-
struct.priority
|
|
218
|
-
struct.nice
|
|
219
|
-
|
|
220
|
-
struct.itrealvalue
|
|
221
|
-
struct.starttime
|
|
222
|
-
struct.vsize
|
|
223
|
-
struct.rss
|
|
224
|
-
struct.rlim
|
|
225
|
-
struct.
|
|
226
|
-
struct.
|
|
227
|
-
struct.
|
|
228
|
-
struct.
|
|
229
|
-
struct.
|
|
230
|
-
struct.
|
|
231
|
-
struct.
|
|
232
|
-
struct.
|
|
233
|
-
struct.
|
|
234
|
-
struct.
|
|
235
|
-
struct.
|
|
236
|
-
struct.
|
|
237
|
-
struct.
|
|
238
|
-
struct.
|
|
239
|
-
struct.
|
|
240
|
-
struct.
|
|
205
|
+
struct.pid = stat[0].to_i
|
|
206
|
+
struct.comm = stat[1].tr('()','') # Remove parens
|
|
207
|
+
struct.state = stat[2]
|
|
208
|
+
struct.ppid = stat[3].to_i
|
|
209
|
+
struct.pgrp = stat[4].to_i
|
|
210
|
+
struct.session = stat[5].to_i
|
|
211
|
+
struct.tty_nr = stat[6].to_i
|
|
212
|
+
struct.tpgid = stat[7].to_i
|
|
213
|
+
struct.flags = stat[8].to_i
|
|
214
|
+
struct.minflt = stat[9].to_i
|
|
215
|
+
struct.cminflt = stat[10].to_i
|
|
216
|
+
struct.majflt = stat[11].to_i
|
|
217
|
+
struct.cmajflt = stat[12].to_i
|
|
218
|
+
struct.utime = stat[13].to_i
|
|
219
|
+
struct.stime = stat[14].to_i
|
|
220
|
+
struct.cutime = stat[15].to_i
|
|
221
|
+
struct.cstime = stat[16].to_i
|
|
222
|
+
struct.priority = stat[17].to_i
|
|
223
|
+
struct.nice = stat[18].to_i
|
|
224
|
+
struct.num_threads = stat[19].to_i
|
|
225
|
+
struct.itrealvalue = stat[20].to_i
|
|
226
|
+
struct.starttime = stat[21].to_i
|
|
227
|
+
struct.vsize = stat[22].to_i
|
|
228
|
+
struct.rss = stat[23].to_i
|
|
229
|
+
struct.rlim = stat[24].to_i # Old name for backwards compatibility
|
|
230
|
+
struct.rsslim = stat[24].to_i
|
|
231
|
+
struct.startcode = stat[25].to_i
|
|
232
|
+
struct.endcode = stat[26].to_i
|
|
233
|
+
struct.startstack = stat[27].to_i
|
|
234
|
+
struct.kstkesp = stat[28].to_i
|
|
235
|
+
struct.kstkeip = stat[29].to_i
|
|
236
|
+
struct.signal = stat[30].to_i
|
|
237
|
+
struct.blocked = stat[31].to_i
|
|
238
|
+
struct.sigignore = stat[32].to_i
|
|
239
|
+
struct.sigcatch = stat[33].to_i
|
|
240
|
+
struct.wchan = stat[34].to_i
|
|
241
|
+
struct.nswap = stat[35].to_i
|
|
242
|
+
struct.cnswap = stat[36].to_i
|
|
243
|
+
struct.exit_signal = stat[37].to_i
|
|
244
|
+
struct.processor = stat[38].to_i
|
|
245
|
+
struct.rt_priority = stat[39].to_i
|
|
246
|
+
struct.policy = stat[40].to_i
|
|
247
|
+
struct.delayacct_blkio_ticks = stat[41].to_i
|
|
248
|
+
struct.guest_time = stat[42].to_i
|
|
249
|
+
struct.cguest_time = stat[43].to_i
|
|
241
250
|
|
|
242
251
|
# Get /proc/<pid>/status information (name, uid, euid, gid, egid)
|
|
243
252
|
begin
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
RSpec.configure do |config|
|
|
2
|
+
config.filter_run_excluding :skip_jruby if RUBY_PLATFORM == 'java'
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
if RUBY_PLATFORM == 'java'
|
|
6
|
+
require 'ffi'
|
|
7
|
+
module Exec
|
|
8
|
+
extend FFI::Library
|
|
9
|
+
ffi_lib FFI::Library::LIBC
|
|
10
|
+
attach_function :fork, [], :int
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def fork
|
|
14
|
+
pid = Exec.fork
|
|
15
|
+
if pid == 0
|
|
16
|
+
yield if block_given?
|
|
17
|
+
return nil
|
|
18
|
+
else
|
|
19
|
+
return pid
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -16,7 +16,8 @@ describe Sys::ProcTable do
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
it "has a VERSION constant set to the expected value" do
|
|
19
|
-
expect(Sys::ProcTable::VERSION).to eql('1.2.
|
|
19
|
+
expect(Sys::ProcTable::VERSION).to eql('1.2.5')
|
|
20
|
+
expect(Sys::ProcTable::VERSION).to be_frozen
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
it "defines a custom error class" do
|
|
@@ -87,30 +87,30 @@ describe Sys::ProcTable do
|
|
|
87
87
|
expect(subject.svgid).to eql(Process.gid)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
it "contains a comm member and returns the expected value" do
|
|
90
|
+
it "contains a comm member and returns the expected value", :skip_jruby do
|
|
91
91
|
expect(subject).to respond_to(:comm)
|
|
92
92
|
expect(subject.comm).to be_kind_of(String)
|
|
93
93
|
expect(subject.comm).to eql('sleep')
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
it "contains a cmdline member and returns the expected value" do
|
|
96
|
+
it "contains a cmdline member and returns the expected value", :skip_jruby do
|
|
97
97
|
expect(subject).to respond_to(:cmdline)
|
|
98
98
|
expect(subject.cmdline).to be_kind_of(String)
|
|
99
99
|
expect(subject.cmdline).to eql('sleep 60')
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
it "returns a string with the expected arguments for the cmdline member" do
|
|
102
|
+
it "returns a string with the expected arguments for the cmdline member", :skip_jruby do
|
|
103
103
|
ptable = Sys::ProcTable.ps(pid: @pid2)
|
|
104
104
|
expect(ptable.cmdline).to eql('ruby -Ilib -e sleep \'120\'.to_i -- foo bar')
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
it "contains an exe member and returns the expected value" do
|
|
107
|
+
it "contains an exe member and returns the expected value", :skip_jruby do
|
|
108
108
|
expect(subject).to respond_to(:exe)
|
|
109
109
|
expect(subject.exe).to be_kind_of(String)
|
|
110
110
|
expect(subject.exe).to eql(`which sleep`.chomp)
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
it "contains an environ member and returns the expected value" do
|
|
113
|
+
it "contains an environ member and returns the expected value", :skip_jruby do
|
|
114
114
|
expect(subject).to respond_to(:environ)
|
|
115
115
|
expect(subject.environ).to be_kind_of(Hash)
|
|
116
116
|
expect(subject.environ['A']).to eql('B')
|