sys-proctable 1.2.0 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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')