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.
@@ -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
- raise TypeError unless pid.is_a?(Numeric) if pid
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
- pids.each do |lpid|
189
- next unless pid == lpid if pid
184
+ if pid
185
+ raise TypeError unless pid.is_a?(Numeric)
190
186
  info = ProcTaskAllInfo.new
191
187
 
192
- nb = proc_pidinfo(lpid, PROC_PIDTASKALLINFO, 0, info, info.size)
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
- next # Either we don't have permission, or the pid no longer exists
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
- # Avoid potentially invalid data
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(lpid, struct)
209
- get_thread_info(lpid, struct, info[:ptinfo]) unless kwargs[:thread_info] == false
210
-
211
- # Chop the leading xx_ from the FFI struct members for our ruby struct.
212
- info.members.each do |nested|
213
- info[nested].members.each do |member|
214
- if info[nested][member].is_a?(FFI::StructLayout::CharArray)
215
- struct[PROC_STRUCT_FIELD_MAP[member]] = info[nested][member].to_s
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
- struct[PROC_STRUCT_FIELD_MAP[member]] = info[nested][member]
230
+ raise SystemCallError.new('proc_pidinfo', FFI.errno)
218
231
  end
219
232
  end
220
- end
221
233
 
222
- struct.freeze
234
+ # Avoid potentially invalid data
235
+ next if nb != info.size
223
236
 
224
- if block_given?
225
- yield struct
226
- else
227
- array << struct
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
- return nil if array.nil?
232
- pid ? array.first : array
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
- nb = proc_pidinfo(pid, PROC_PIDTHREADINFO, buf[index].read_uint64, tinfo, tinfo.size)
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)
@@ -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', # 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
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 = 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
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
@@ -1,6 +1,6 @@
1
1
  module Sys
2
2
  class ProcTable
3
3
  # The version of the sys-proctable library
4
- VERSION = '1.2.0'.freeze
4
+ VERSION = '1.2.5'.freeze
5
5
  end
6
6
  end
@@ -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.0')
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')