sys-proctable 1.2.6 → 1.2.7

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.
@@ -11,21 +11,32 @@ module Sys
11
11
  # There is no constructor
12
12
  private_class_method :new
13
13
 
14
- private
15
-
16
14
  PROC_PIDTASKALLINFO = 2
17
15
  PROC_PIDTHREADINFO = 5
18
16
  PROC_PIDLISTTHREADS = 6
19
17
 
18
+ private_constant :PROC_PIDTASKALLINFO
19
+ private_constant :PROC_PIDTHREADINFO
20
+ private_constant :PROC_PIDLISTTHREADS
21
+
20
22
  CTL_KERN = 1
21
23
  KERN_PROCARGS = 38
22
24
  KERN_PROCARGS2 = 49
23
25
  MAXCOMLEN = 16
24
26
  MAXPATHLEN = 256
25
27
 
28
+ private_constant :CTL_KERN
29
+ private_constant :KERN_PROCARGS
30
+ private_constant :KERN_PROCARGS2
31
+ private_constant :MAXCOMLEN
32
+ private_constant :MAXPATHLEN
33
+
26
34
  MAXTHREADNAMESIZE = 64
27
35
  PROC_PIDPATHINFO_MAXSIZE = MAXPATHLEN * 4
28
36
 
37
+ private_constant :MAXTHREADNAMESIZE
38
+ private_constant :PROC_PIDPATHINFO_MAXSIZE
39
+
29
40
  # JRuby/Truffleruby on Mac
30
41
  unless defined? FFI::StructLayout::CharArray
31
42
  if defined? FFI::StructLayout::CharArrayProxy
@@ -62,6 +73,8 @@ module Sys
62
73
  )
63
74
  end
64
75
 
76
+ private_constant :ProcBsdInfo
77
+
65
78
  class ProcTaskInfo < FFI::Struct
66
79
  layout(
67
80
  :pti_virtual_size, :uint64_t,
@@ -85,6 +98,8 @@ module Sys
85
98
  )
86
99
  end
87
100
 
101
+ private_constant :ProcTaskInfo
102
+
88
103
  class ProcThreadInfo < FFI::Struct
89
104
  layout(
90
105
  :pth_user_time, :uint64_t,
@@ -101,28 +116,37 @@ module Sys
101
116
  )
102
117
  end
103
118
 
119
+ private_constant :ProcThreadInfo
120
+
104
121
  # Map the fields from the FFI::Structs to the Sys::ProcTable struct on
105
122
  # class load to reduce the amount of objects needing to be generated for
106
123
  # each invocation of Sys::ProcTable.ps
107
- all_members = ProcBsdInfo.members + ProcTaskInfo.members + ProcThreadInfo.members
108
- PROC_STRUCT_FIELD_MAP = all_members.map { |member|
124
+ all_members = ProcBsdInfo.members + ProcTaskInfo.members + ProcThreadInfo.members
125
+
126
+ PROC_STRUCT_FIELD_MAP = all_members.map do |member|
109
127
  temp = member.to_s.split('_')
110
128
  sproperty = temp.size > 1 ? temp[1..-1].join('_') : temp.first
111
129
  [member, sproperty.to_sym]
112
- }.to_h
130
+ end.to_h
113
131
 
114
132
  class ProcTaskAllInfo < FFI::Struct
115
133
  layout(:pbsd, ProcBsdInfo, :ptinfo, ProcTaskInfo)
116
134
  end
117
135
 
136
+ private_constant :ProcTaskAllInfo
137
+
118
138
  ffi_lib 'proc'
119
139
 
120
- attach_function :proc_listallpids, [:pointer, :int], :int
121
- attach_function :proc_pidinfo, [:int, :int, :uint64_t, :pointer, :int], :int
140
+ attach_function :proc_listallpids, %i[pointer int], :int
141
+ attach_function :proc_pidinfo, %i[int int uint64_t pointer int], :int
122
142
 
123
143
  ffi_lib FFI::Library::LIBC
124
144
 
125
- attach_function :sysctl, [:pointer, :uint, :pointer, :pointer, :pointer, :size_t], :int
145
+ attach_function :sysctl, %i[pointer uint pointer pointer pointer size_t], :int
146
+
147
+ private_class_method :proc_listallpids
148
+ private_class_method :proc_pidinfo
149
+ private_class_method :sysctl
126
150
 
127
151
  # These mostly mimic the struct members, but we've added a few custom ones as well.
128
152
  @fields = %w[
@@ -136,16 +160,18 @@ module Sys
136
160
 
137
161
  # Add a couple aliases to make it similar to Linux
138
162
  ProcTableStruct = Struct.new("ProcTableStruct", *@fields) do
139
- alias vsize virtual_size
140
- alias rss resident_size
163
+ alias_method :vsize, :virtual_size
164
+ alias_method :rss, :resident_size
141
165
  end
142
166
 
167
+ private_constant :ProcTableStruct
168
+
143
169
  ThreadInfoStruct = Struct.new("ThreadInfo", :user_time, :system_time,
144
170
  :cpu_usage, :policy, :run_state, :flags, :sleep_time, :curpri,
145
171
  :priority, :maxpriority, :name
146
172
  )
147
173
 
148
- public
174
+ private_constant :ThreadInfoStruct
149
175
 
150
176
  # Returns an array of fields that each ProcTableStruct will contain. This
151
177
  # may be useful if you want to know in advance what fields are available
@@ -222,7 +248,7 @@ module Sys
222
248
  array = block_given? ? nil : []
223
249
 
224
250
  pids.each do |lpid|
225
- next unless pid == lpid if pid
251
+ next if pid && pid != lpid
226
252
  info = ProcTaskAllInfo.new
227
253
 
228
254
  nb = proc_pidinfo(lpid, PROC_PIDTASKALLINFO, 0, info, info.size)
@@ -258,8 +284,6 @@ module Sys
258
284
  end
259
285
  end
260
286
 
261
- private
262
-
263
287
  # Pass by reference method that updates the Ruby struct based on the FFI struct.
264
288
  #
265
289
  def self.apply_info_to_struct(info, struct)
@@ -275,6 +299,8 @@ module Sys
275
299
  end
276
300
  end
277
301
 
302
+ private_class_method :apply_info_to_struct
303
+
278
304
  # Returns an array of ThreadInfo objects for the given pid.
279
305
  #
280
306
  def self.get_thread_info(pid, struct, ptinfo)
@@ -292,7 +318,7 @@ module Sys
292
318
  max = ptinfo[:pti_threadnum]
293
319
  struct[:threadinfo] = []
294
320
 
295
- 0.upto(max-1) do |index|
321
+ 0.upto(max - 1) do |index|
296
322
  tinfo = ProcThreadInfo.new
297
323
 
298
324
  # Use read_array_of_uint64 for compatibility with JRuby if necessary.
@@ -304,7 +330,7 @@ module Sys
304
330
 
305
331
  if nb <= 0
306
332
  if [Errno::EPERM::Errno, Errno::ESRCH::Errno].include?(FFI.errno)
307
- return # Either we don't have permission, or the pid no longer exists
333
+ next # Either we don't have permission, or the pid no longer exists
308
334
  else
309
335
  raise SystemCallError.new('proc_pidinfo', FFI.errno)
310
336
  end
@@ -321,15 +347,21 @@ module Sys
321
347
  tinfo[:pth_curpri],
322
348
  tinfo[:pth_priority],
323
349
  tinfo[:pth_maxpriority],
324
- tinfo[:pth_name].to_s,
350
+ tinfo[:pth_name].to_s
325
351
  )
326
352
 
327
353
  struct[:threadinfo] << tinfo_struct
328
354
  end
329
355
  end
330
356
 
357
+ private_class_method :get_thread_info
358
+
331
359
  # Get the command line arguments, as well as the environment settings,
332
360
  # for the given PID.
361
+ #--
362
+ # Note that on Big Sur and later it seems that you cannot get environment
363
+ # variable information on spawned processes except in certain circumstances,
364
+ # e.g. SIP has been disabled, the kernel is in debug mode, etc.
333
365
  #
334
366
  def self.get_cmd_args_and_env(pid, struct)
335
367
  len = FFI::MemoryPointer.new(:size_t)
@@ -389,22 +421,23 @@ module Sys
389
421
 
390
422
  # Extract the full command line and its arguments from the array
391
423
  argc.times do
392
- cmdline << ' ' + array.shift
424
+ cmdline << ' ' << array.shift
393
425
  end
394
426
 
395
427
  struct[:cmdline] = cmdline.strip
396
428
 
397
429
  # Anything remaining at this point is a collection of key=value
398
430
  # pairs which we convert into a hash.
399
- environ = array.inject({}) do |hash, string|
431
+ environ = array.each_with_object({}) do |string, hash|
400
432
  if string && string.include?('=')
401
433
  key, value = string.split('=')
402
434
  hash[key] = value
403
435
  end
404
- hash
405
436
  end
406
437
 
407
438
  struct[:environ] = environ
408
439
  end
440
+
441
+ private_class_method :get_cmd_args_and_env
409
442
  end
410
443
  end
@@ -77,7 +77,7 @@ module Sys
77
77
  layout(
78
78
  :ar_ref, :uint,
79
79
  :ar_length, :uint,
80
- :ar_args, [:uchar,1]
80
+ :ar_args, [:uchar, 1]
81
81
  )
82
82
  end
83
83
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sys
2
4
  class ProcTable
3
5
  # This represents a cgroup entry
@@ -20,7 +22,7 @@ module Sys
20
22
  def initialize(string)
21
23
  @string = string.chomp
22
24
  @fields = @string.split(/:/)
23
- rescue
25
+ rescue StandardError
24
26
  @fields = []
25
27
  end
26
28
 
@@ -32,7 +34,7 @@ module Sys
32
34
  # Return sets of subsystems bound to the hierarchy
33
35
  def subsystems
34
36
  @fields[1].split(/,/)
35
- rescue
37
+ rescue StandardError
36
38
  []
37
39
  end
38
40
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sys
2
4
  class ProcTable
3
5
  # Smaps represents a process' memory size for all mapped files
@@ -25,7 +27,18 @@ module Sys
25
27
  # Example:
26
28
  #
27
29
  # 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>
30
+ #
31
+ # # result
32
+ #
33
+ # #<Sys::ProcTable::Smaps:0x007f8ac5930768
34
+ # @pid=123,
35
+ # @pss=107000,
36
+ # @rss=368000,
37
+ # @uss=96000,
38
+ # @swap=192000,
39
+ # @vss=136752000
40
+ # >
41
+ #
29
42
  # smaps.pss # => 109568
30
43
  # smaps.rss # => 376832
31
44
  # smaps.uss # => 98304
@@ -33,7 +46,6 @@ module Sys
33
46
  # smaps.vss # => 140034048
34
47
  #
35
48
  class Smaps
36
-
37
49
  # Process ID for this smaps
38
50
  attr_reader :pid
39
51
 
@@ -51,19 +63,19 @@ module Sys
51
63
  # 4k + (4k / 2) + (3k / 3) = 7k
52
64
  #
53
65
  attr_reader :pss
54
- alias_method :proportional_set_size, :pss
66
+ alias proportional_set_size pss
55
67
 
56
68
  # Resident set size
57
69
  #
58
70
  # RSS is the total size of all pages, shared or not, mapped to a process.
59
71
  attr_reader :rss
60
- alias_method :resident_set_size, :rss
72
+ alias resident_set_size rss
61
73
 
62
74
  # Unique set size
63
75
  #
64
76
  # USS is the total size of all private pages mapped to a process.
65
77
  attr_reader :uss
66
- alias_method :unique_set_size, :uss
78
+ alias unique_set_size uss
67
79
 
68
80
  # Swap
69
81
  #
@@ -76,7 +88,7 @@ module Sys
76
88
  # lazily loaded, this value represents the total size of all mapped files
77
89
  # if they were all loaded.
78
90
  attr_reader :vss
79
- alias_method :virtual_set_size, :vss
91
+ alias virtual_set_size vss
80
92
 
81
93
  # Create a new smaps object
82
94
  #
@@ -95,22 +107,22 @@ module Sys
95
107
  smaps_contents.each_line { |line| parse_smaps_line(line) }
96
108
  end
97
109
 
98
- alias_method :to_s, :inspect
110
+ alias to_s inspect
99
111
 
100
112
  private
101
113
 
102
114
  def parse_smaps_line(line)
103
115
  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
116
+ when /^Pss:\s+?(\d+)/
117
+ @pss += Regexp.last_match[1].to_i * 1000
118
+ when /^Rss:\s+?(\d+)/
119
+ @rss += Regexp.last_match[1].to_i * 1000
120
+ when /^Size:\s+?(\d+)/
121
+ @vss += Regexp.last_match[1].to_i * 1000
122
+ when /^Swap:\s+?(\d+)/
123
+ @swap += Regexp.last_match[1].to_i * 1000
124
+ when /^Private_(Clean|Dirty):\s+?(\d+)/
125
+ @uss += Regexp.last_match[2].to_i * 1000
114
126
  end
115
127
  end
116
128
  end
@@ -1,23 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sys/proctable/version'
2
4
  require_relative 'proctable/cgroup_entry'
3
5
  require_relative 'proctable/smaps'
4
6
 
5
7
  # The Sys module serves as a namespace only.
6
8
  module Sys
7
-
8
9
  # The ProcTable class encapsulates process table information.
9
10
  class ProcTable
10
-
11
11
  # Error typically raised if the ProcTable.ps method fails.
12
12
  class Error < StandardError; end
13
13
 
14
14
  # There is no constructor
15
15
  private_class_method :new
16
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
17
+ @mem_total = File.read("/proc/meminfo")[/MemTotal.*/].split[1].to_i * 1024 rescue nil
18
+ @boot_time = File.read("/proc/stat")[/btime.*/].split.last.to_i rescue nil
21
19
 
22
20
  @fields = [
23
21
  'cmdline', # Complete command line
@@ -83,10 +81,10 @@ module Sys
83
81
  'smaps' # Process memory size for all mapped files
84
82
  ]
85
83
 
86
- public
87
-
88
84
  ProcTableStruct = Struct.new('ProcTableStruct', *@fields)
89
85
 
86
+ private_constant :ProcTableStruct
87
+
90
88
  # In block form, yields a ProcTableStruct for each process entry that you
91
89
  # have rights to. This method returns an array of ProcTableStruct's in
92
90
  # non-block form.
@@ -121,19 +119,19 @@ module Sys
121
119
  array = block_given? ? nil : []
122
120
  struct = nil
123
121
 
124
- raise TypeError unless pid.is_a?(Numeric) if pid
122
+ raise TypeError if pid && !pid.is_a?(Numeric)
125
123
 
126
- Dir.foreach("/proc"){ |file|
124
+ Dir.foreach("/proc") do |file|
127
125
  next if file =~ /\D/ # Skip non-numeric directories
128
- next unless file.to_i == pid if pid
126
+ next if pid && file.to_i != pid
129
127
 
130
128
  struct = ProcTableStruct.new
131
129
 
132
130
  # Get /proc/<pid>/cmdline information. Strip out embedded nulls.
133
131
  begin
134
- data = IO.read("/proc/#{file}/cmdline").tr("\000", ' ').strip
132
+ data = File.read("/proc/#{file}/cmdline").tr("\000", ' ').strip
135
133
  struct.cmdline = data
136
- rescue
134
+ rescue StandardError
137
135
  next # Process terminated, on to the next process
138
136
  end
139
137
 
@@ -146,10 +144,10 @@ module Sys
146
144
  struct.environ = {}
147
145
 
148
146
  begin
149
- IO.read("/proc/#{file}/environ").force_encoding("UTF-8").split("\0").each{ |str|
147
+ File.read("/proc/#{file}/environ").force_encoding("UTF-8").split("\0").each do |str|
150
148
  key, value = str.split('=')
151
149
  struct.environ[key] = value
152
- }
150
+ end
153
151
  rescue Errno::EACCES, Errno::ESRCH, Errno::ENOENT
154
152
  # Ignore and move on.
155
153
  end
@@ -166,7 +164,7 @@ module Sys
166
164
  Dir["/proc/#{file}/fd/*"].each do |fd|
167
165
  struct.fd[File.basename(fd)] = File.readlink(fd) rescue nil
168
166
  end
169
- rescue
167
+ rescue StandardError
170
168
  # Ignore and move on
171
169
  end
172
170
 
@@ -174,7 +172,7 @@ module Sys
174
172
  struct.root = File.readlink("/proc/#{file}/root") rescue nil
175
173
 
176
174
  # Get /proc/<pid>/stat information
177
- stat = IO.read("/proc/#{file}/stat") rescue next
175
+ stat = File.read("/proc/#{file}/stat") rescue next
178
176
 
179
177
  # Get number of LWP, one directory for each in /proc/<pid>/task/
180
178
  # Every process has at least one thread, so if we fail to read the task directory, set nlwp to 1.
@@ -182,7 +180,7 @@ module Sys
182
180
 
183
181
  # Get control groups to which the process belongs
184
182
  unless cgroup == false
185
- struct.cgroup = IO.readlines("/proc/#{file}/cgroup").map { |l| CgroupEntry.new(l) } rescue []
183
+ struct.cgroup = File.readlines("/proc/#{file}/cgroup").map { |l| CgroupEntry.new(l) } rescue []
186
184
  end
187
185
 
188
186
  # Read smaps, returning a parsable string if we don't have permissions.
@@ -190,12 +188,18 @@ module Sys
190
188
  # are true for a file in the /proc fileystem but raises a Errno:EACCESS
191
189
  # when your try to read it without permissions.
192
190
  unless smaps == false
193
- smaps_contents = IO.read("/proc/#{file}/smaps") rescue ""
191
+ smaps_contents = File.read("/proc/#{file}/smaps") rescue ""
194
192
  struct.smaps = Smaps.new(file, smaps_contents)
195
193
  end
196
194
 
197
- # Deal with spaces in comm name. Courtesy of Ara Howard.
198
- re = %r/\([^\)]+\)/
195
+ # Deal with spaces in comm name. This isn't supposed to happen, but in
196
+ # rare cases - the original offending case was "(xzen thread)" - it can
197
+ # occur. So we parse it out, replace the spaces with hyphens, and
198
+ # re-insert it into the stat string so that it splits properly later on.
199
+ #
200
+ # Courtesy of Ara Howard.
201
+ #
202
+ re = /\([^)]+\)/
199
203
  comm = stat[re]
200
204
  comm.tr!(' ', '-')
201
205
  stat[re] = comm
@@ -203,7 +207,7 @@ module Sys
203
207
  stat = stat.split
204
208
 
205
209
  struct.pid = stat[0].to_i
206
- struct.comm = stat[1].tr('()','') # Remove parens
210
+ struct.comm = stat[1].tr('()', '') # Remove parens
207
211
  struct.state = stat[2]
208
212
  struct.ppid = stat[3].to_i
209
213
  struct.pgrp = stat[4].to_i
@@ -250,16 +254,16 @@ module Sys
250
254
 
251
255
  # Get /proc/<pid>/status information (name, uid, euid, gid, egid)
252
256
  begin
253
- IO.foreach("/proc/#{file}/status") do |line|
257
+ File.foreach("/proc/#{file}/status") do |line|
254
258
  case line
255
259
  when /Name:\s*?(\w+)/
256
- struct.name = $1
260
+ struct.name = Regexp.last_match(1)
257
261
  when /Uid:\s*?(\d+)\s*?(\d+)/
258
- struct.uid = $1.to_i
259
- struct.euid = $2.to_i
262
+ struct.uid = Regexp.last_match(1).to_i
263
+ struct.euid = Regexp.last_match(2).to_i
260
264
  when /Gid:\s*?(\d+)\s*?(\d+)/
261
- struct.gid = $1.to_i
262
- struct.egid = $2.to_i
265
+ struct.gid = Regexp.last_match(1).to_i
266
+ struct.egid = Regexp.last_match(2).to_i
263
267
  end
264
268
  end
265
269
  rescue Errno::ESRCH, Errno::ENOENT
@@ -280,7 +284,7 @@ module Sys
280
284
  else
281
285
  array << struct
282
286
  end
283
- }
287
+ end
284
288
 
285
289
  pid ? struct : array
286
290
  end
@@ -295,21 +299,21 @@ module Sys
295
299
  # puts "Field: #{field}"
296
300
  # }
297
301
  #
298
- def self.fields
299
- @fields
302
+ class << self
303
+ attr_reader :fields
300
304
  end
301
305
 
302
- private
303
-
304
306
  # Calculate the percentage of memory usage for the given process.
305
307
  #
306
308
  def self.get_pctmem(rss)
307
309
  return nil unless @mem_total
308
310
  page_size = 4096
309
311
  rss_total = rss * page_size
310
- sprintf("%3.2f", (rss_total.to_f / @mem_total) * 100).to_f
312
+ format("%3.2f", (rss_total.to_f / @mem_total) * 100).to_f
311
313
  end
312
314
 
315
+ private_class_method :get_pctmem
316
+
313
317
  # Calculate the percentage of CPU usage for the given process.
314
318
  #
315
319
  def self.get_pctcpu(utime, start_time)
@@ -317,7 +321,9 @@ module Sys
317
321
  hertz = 100.0
318
322
  utime = (utime * 10000).to_f
319
323
  stime = (start_time.to_f / hertz) + @boot_time
320
- sprintf("%3.2f", (utime / 10000.0) / (Time.now.to_i - stime)).to_f
324
+ format("%3.2f", (utime / 10000.0) / (Time.now.to_i - stime)).to_f
321
325
  end
326
+
327
+ private_class_method :get_pctcpu
322
328
  end
323
329
  end
@@ -110,7 +110,7 @@ module Sys
110
110
  :pr_slptime, Timeval,
111
111
  :pr_wtime, Timeval,
112
112
  :pr_stoptime, Timeval,
113
- :pr_filetime, [Timeval,6],
113
+ :pr_filetime, [Timeval, 6],
114
114
  :pr_minf, :ulong_t,
115
115
  :pr_majf, :ulong_t,
116
116
  :pr_nswap, :ulong_t,
@@ -343,7 +343,7 @@ module Sys
343
343
  env_address += data.length + 1 # Add 1 for the space
344
344
  end
345
345
  end
346
- rescue Errno::EACCES, Errno::EOVERFLOW, EOFError, RangeError
346
+ rescue Errno::EACCES, Errno::EBADF, Errno::EOVERFLOW, EOFError, RangeError
347
347
  # Skip this if we don't have proper permissions, if there's
348
348
  # no associated environment, or if there's a largefile issue.
349
349
  rescue Errno::ENOENT
@@ -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.6'.freeze
4
+ VERSION = '1.2.7'.freeze
5
5
  end
6
6
  end
data/lib/sys/top.rb CHANGED
@@ -15,7 +15,7 @@ module Sys
15
15
  #
16
16
  # Exception: the default sort field is 'pid' on AIX, Darwin and Windows.
17
17
  #
18
- def self.top(num=10, field='pctcpu')
18
+ def self.top(num = 10, field = 'pctcpu')
19
19
  field = field.to_s if field.is_a?(Symbol)
20
20
 
21
21
  aix = RbConfig::CONFIG['host_os'] =~ /aix/i