sys-proctable 1.2.5 → 1.2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,24 +11,39 @@ 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
 
29
- # JRuby on Mac
37
+ private_constant :MAXTHREADNAMESIZE
38
+ private_constant :PROC_PIDPATHINFO_MAXSIZE
39
+
40
+ # JRuby/Truffleruby on Mac
30
41
  unless defined? FFI::StructLayout::CharArray
31
- FFI::StructLayout::CharArray = FFI::StructLayout::CharArrayProxy
42
+ if defined? FFI::StructLayout::CharArrayProxy
43
+ FFI::StructLayout::CharArray = FFI::StructLayout::CharArrayProxy
44
+ else
45
+ FFI::StructLayout::CharArray = FFI::Struct::CharArray
46
+ end
32
47
  end
33
48
 
34
49
  class ProcBsdInfo < FFI::Struct
@@ -58,6 +73,8 @@ module Sys
58
73
  )
59
74
  end
60
75
 
76
+ private_constant :ProcBsdInfo
77
+
61
78
  class ProcTaskInfo < FFI::Struct
62
79
  layout(
63
80
  :pti_virtual_size, :uint64_t,
@@ -81,6 +98,8 @@ module Sys
81
98
  )
82
99
  end
83
100
 
101
+ private_constant :ProcTaskInfo
102
+
84
103
  class ProcThreadInfo < FFI::Struct
85
104
  layout(
86
105
  :pth_user_time, :uint64_t,
@@ -97,28 +116,37 @@ module Sys
97
116
  )
98
117
  end
99
118
 
119
+ private_constant :ProcThreadInfo
120
+
100
121
  # Map the fields from the FFI::Structs to the Sys::ProcTable struct on
101
122
  # class load to reduce the amount of objects needing to be generated for
102
123
  # each invocation of Sys::ProcTable.ps
103
- all_members = ProcBsdInfo.members + ProcTaskInfo.members + ProcThreadInfo.members
104
- 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|
105
127
  temp = member.to_s.split('_')
106
128
  sproperty = temp.size > 1 ? temp[1..-1].join('_') : temp.first
107
129
  [member, sproperty.to_sym]
108
- }.to_h
130
+ end.to_h
109
131
 
110
132
  class ProcTaskAllInfo < FFI::Struct
111
133
  layout(:pbsd, ProcBsdInfo, :ptinfo, ProcTaskInfo)
112
134
  end
113
135
 
136
+ private_constant :ProcTaskAllInfo
137
+
114
138
  ffi_lib 'proc'
115
139
 
116
- attach_function :proc_listallpids, [:pointer, :int], :int
117
- 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
118
142
 
119
143
  ffi_lib FFI::Library::LIBC
120
144
 
121
- 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
122
150
 
123
151
  # These mostly mimic the struct members, but we've added a few custom ones as well.
124
152
  @fields = %w[
@@ -132,16 +160,18 @@ module Sys
132
160
 
133
161
  # Add a couple aliases to make it similar to Linux
134
162
  ProcTableStruct = Struct.new("ProcTableStruct", *@fields) do
135
- alias vsize virtual_size
136
- alias rss resident_size
163
+ alias_method :vsize, :virtual_size
164
+ alias_method :rss, :resident_size
137
165
  end
138
166
 
167
+ private_constant :ProcTableStruct
168
+
139
169
  ThreadInfoStruct = Struct.new("ThreadInfo", :user_time, :system_time,
140
170
  :cpu_usage, :policy, :run_state, :flags, :sleep_time, :curpri,
141
171
  :priority, :maxpriority, :name
142
172
  )
143
173
 
144
- public
174
+ private_constant :ThreadInfoStruct
145
175
 
146
176
  # Returns an array of fields that each ProcTableStruct will contain. This
147
177
  # may be useful if you want to know in advance what fields are available
@@ -218,7 +248,7 @@ module Sys
218
248
  array = block_given? ? nil : []
219
249
 
220
250
  pids.each do |lpid|
221
- next unless pid == lpid if pid
251
+ next if pid && pid != lpid
222
252
  info = ProcTaskAllInfo.new
223
253
 
224
254
  nb = proc_pidinfo(lpid, PROC_PIDTASKALLINFO, 0, info, info.size)
@@ -254,8 +284,6 @@ module Sys
254
284
  end
255
285
  end
256
286
 
257
- private
258
-
259
287
  # Pass by reference method that updates the Ruby struct based on the FFI struct.
260
288
  #
261
289
  def self.apply_info_to_struct(info, struct)
@@ -271,6 +299,8 @@ module Sys
271
299
  end
272
300
  end
273
301
 
302
+ private_class_method :apply_info_to_struct
303
+
274
304
  # Returns an array of ThreadInfo objects for the given pid.
275
305
  #
276
306
  def self.get_thread_info(pid, struct, ptinfo)
@@ -288,7 +318,7 @@ module Sys
288
318
  max = ptinfo[:pti_threadnum]
289
319
  struct[:threadinfo] = []
290
320
 
291
- 0.upto(max-1) do |index|
321
+ 0.upto(max - 1) do |index|
292
322
  tinfo = ProcThreadInfo.new
293
323
 
294
324
  # Use read_array_of_uint64 for compatibility with JRuby if necessary.
@@ -300,7 +330,7 @@ module Sys
300
330
 
301
331
  if nb <= 0
302
332
  if [Errno::EPERM::Errno, Errno::ESRCH::Errno].include?(FFI.errno)
303
- 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
304
334
  else
305
335
  raise SystemCallError.new('proc_pidinfo', FFI.errno)
306
336
  end
@@ -317,15 +347,21 @@ module Sys
317
347
  tinfo[:pth_curpri],
318
348
  tinfo[:pth_priority],
319
349
  tinfo[:pth_maxpriority],
320
- tinfo[:pth_name].to_s,
350
+ tinfo[:pth_name].to_s
321
351
  )
322
352
 
323
353
  struct[:threadinfo] << tinfo_struct
324
354
  end
325
355
  end
326
356
 
357
+ private_class_method :get_thread_info
358
+
327
359
  # Get the command line arguments, as well as the environment settings,
328
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.
329
365
  #
330
366
  def self.get_cmd_args_and_env(pid, struct)
331
367
  len = FFI::MemoryPointer.new(:size_t)
@@ -385,22 +421,23 @@ module Sys
385
421
 
386
422
  # Extract the full command line and its arguments from the array
387
423
  argc.times do
388
- cmdline << ' ' + array.shift
424
+ cmdline << ' ' << array.shift
389
425
  end
390
426
 
391
427
  struct[:cmdline] = cmdline.strip
392
428
 
393
429
  # Anything remaining at this point is a collection of key=value
394
430
  # pairs which we convert into a hash.
395
- environ = array.inject({}) do |hash, string|
431
+ environ = array.each_with_object({}) do |string, hash|
396
432
  if string && string.include?('=')
397
433
  key, value = string.split('=')
398
434
  hash[key] = value
399
435
  end
400
- hash
401
436
  end
402
437
 
403
438
  struct[:environ] = environ
404
439
  end
440
+
441
+ private_class_method :get_cmd_args_and_env
405
442
  end
406
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").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.5'.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