sys-proctable 0.7.6 → 1.2.0

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -0
  4. data/CHANGES +165 -0
  5. data/MANIFEST +33 -41
  6. data/README +115 -135
  7. data/Rakefile +94 -0
  8. data/benchmarks/bench_ps.rb +21 -0
  9. data/doc/top.txt +5 -11
  10. data/examples/example_ps.rb +20 -0
  11. data/lib/aix/sys/proctable.rb +458 -0
  12. data/lib/darwin/sys/proctable.rb +363 -0
  13. data/lib/freebsd/sys/proctable.rb +363 -0
  14. data/lib/linux/sys/proctable.rb +314 -0
  15. data/lib/linux/sys/proctable/cgroup_entry.rb +50 -0
  16. data/lib/linux/sys/proctable/smaps.rb +118 -0
  17. data/lib/sunos/sys/proctable.rb +456 -0
  18. data/lib/sys-proctable.rb +1 -0
  19. data/lib/sys-top.rb +1 -0
  20. data/lib/sys/proctable.rb +18 -0
  21. data/lib/sys/proctable/version.rb +6 -0
  22. data/lib/sys/top.rb +28 -19
  23. data/lib/windows/sys/proctable.rb +208 -0
  24. data/spec/sys_proctable_aix_spec.rb +328 -0
  25. data/spec/sys_proctable_all_spec.rb +89 -0
  26. data/spec/sys_proctable_darwin_spec.rb +120 -0
  27. data/spec/sys_proctable_freebsd_spec.rb +210 -0
  28. data/spec/sys_proctable_linux_spec.rb +310 -0
  29. data/spec/sys_proctable_sunos_spec.rb +316 -0
  30. data/spec/sys_proctable_windows_spec.rb +317 -0
  31. data/spec/sys_top_spec.rb +51 -0
  32. data/sys-proctable.gemspec +38 -0
  33. metadata +140 -64
  34. metadata.gz.sig +0 -0
  35. data/doc/freebsd.txt +0 -90
  36. data/doc/hpux.txt +0 -77
  37. data/doc/linux.txt +0 -85
  38. data/doc/solaris.txt +0 -99
  39. data/doc/windows.txt +0 -122
  40. data/ext/extconf.rb +0 -98
  41. data/ext/sunos/sunos.c +0 -374
  42. data/ext/sunos/sunos.h +0 -177
  43. data/ext/version.h +0 -2
  44. data/test/tc_all.rb +0 -59
  45. data/test/tc_freebsd.rb +0 -45
  46. data/test/tc_hpux.rb +0 -49
  47. data/test/tc_kvm_bsd.rb +0 -31
  48. data/test/tc_linux.rb +0 -45
  49. data/test/tc_sunos.rb +0 -52
  50. data/test/tc_top.rb +0 -26
  51. data/test/tc_windows.rb +0 -40
  52. data/test/test_memleak.rb +0 -54
@@ -0,0 +1,94 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rbconfig'
5
+ require 'rspec/core/rake_task'
6
+ include RbConfig
7
+
8
+ CLEAN.include('**/*.gem', '**/*.rbc')
9
+
10
+ desc 'Install the sys-proctable library'
11
+ task :install do
12
+ file = nil
13
+ dir = File.join(CONFIG['sitelibdir'], 'sys')
14
+
15
+ Dir.mkdir(dir) unless File.exists?(dir)
16
+
17
+ case CONFIG['host_os']
18
+ when /mswin|win32|msdos|cygwin|mingw|windows/i
19
+ file = 'lib/windows/sys/proctable.rb'
20
+ when /linux/i
21
+ file = 'lib/linux/sys/proctable.rb'
22
+ when /sunos|solaris/i
23
+ file = 'lib/sunos/sys/proctable.rb'
24
+ when /aix/i
25
+ file = 'lib/aix/sys/proctable.rb'
26
+ when /freebsd/i
27
+ file = 'lib/freebsd/sys/proctable.rb'
28
+ when /darwin/i
29
+ file = 'lib/darwin/sys/proctable.rb'
30
+ end
31
+
32
+ cp(file, dir, :verbose => true) if file
33
+ end
34
+
35
+ desc 'Uninstall the sys-proctable library'
36
+ task :uninstall do
37
+ dir = File.join(CONFIG['sitelibdir'], 'sys')
38
+ file = File.join(dir, 'proctable.rb')
39
+ rm(file)
40
+ end
41
+
42
+ desc 'Run the benchmark suite'
43
+ task :bench do
44
+ sh "ruby -Ilib benchmarks/bench_ps.rb"
45
+ end
46
+
47
+ desc 'Run the example program'
48
+ task :example do
49
+ sh 'ruby -Ilib -Iext examples/example_ps.rb'
50
+ end
51
+
52
+ desc 'Run the test suite for the sys-proctable library'
53
+ RSpec::Core::RakeTask.new(:spec) do |t|
54
+ t.pattern = ['spec/sys_proctable_all_spec.rb']
55
+
56
+ case CONFIG['host_os']
57
+ when /aix/i
58
+ t.rspec_opts = '-Ilib/aix'
59
+ t.pattern << 'spec/sys_proctable_aix.rb'
60
+ when /darwin/i
61
+ t.rspec_opts = '-Ilib/darwin'
62
+ t.pattern << 'spec/sys_proctable_darwin_spec.rb'
63
+ when /freebsd/i
64
+ t.rspec_opts = '-Ilib/freebsd'
65
+ t.pattern << 'spec/sys_proctable_freebsd_spec.rb'
66
+ when /linux/i
67
+ t.rspec_opts = '-Ilib/linux'
68
+ t.pattern << 'spec/sys_proctable_linux_spec.rb'
69
+ when /sunos|solaris/i
70
+ t.rspec_opts = '-Ilib/sunos'
71
+ t.pattern << 'spec/sys_proctable_sunos_spec.rb'
72
+ when /mswin|msdos|cygwin|mingw|windows/i
73
+ t.rspec_opts = '-Ilib/windows'
74
+ t.pattern << 'spec/sys_proctable_windows_spec.rb'
75
+ end
76
+ end
77
+
78
+ namespace :gem do
79
+ desc 'Create a gem for the specified OS, or your current OS by default'
80
+ task :create => [:clean] do
81
+ require 'rubygems/package'
82
+ spec = eval(IO.read('sys-proctable.gemspec'))
83
+ spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
84
+ Gem::Package.build(spec, true)
85
+ end
86
+
87
+ desc 'Install the sys-proctable library as a gem'
88
+ task :install => [:create] do
89
+ gem_name = Dir['*.gem'].first
90
+ sh "gem install -l #{gem_name}"
91
+ end
92
+ end
93
+
94
+ task :default => :spec
@@ -0,0 +1,21 @@
1
+ ########################################################################
2
+ # bench_ps.rb
3
+ #
4
+ # Benchmark program to show overall speed and compare the block form
5
+ # versus the non-block form. You should run this benchmark via the
6
+ # 'rake bench' Rake task.
7
+ ########################################################################
8
+ require 'benchmark'
9
+ require 'sys/proctable'
10
+
11
+ MAX = 10
12
+
13
+ Benchmark.bm do |bench|
14
+ bench.report("Block form"){
15
+ MAX.times{ Sys::ProcTable.ps{} }
16
+ }
17
+
18
+ bench.report("Non-block form"){
19
+ MAX.times{ Sys::ProcTable.ps }
20
+ }
21
+ end
@@ -7,9 +7,8 @@
7
7
 
8
8
  = Synopsis
9
9
  require "sys/top"
10
- include Sys
11
10
 
12
- Top.top(5).each{ |ps|
11
+ Sys::Top.top(5).each{ |ps|
13
12
  p ps
14
13
  }
15
14
 
@@ -18,7 +17,7 @@ VERSION
18
17
  Returns the version number of this package as a String.
19
18
 
20
19
  = Class Methods
21
- Top.top(number=10, field="pctcpu")
20
+ Sys::Top.top(number=10, field="pctcpu")
22
21
  Returns an array of ProcTableStruct's. The size of the array (i.e. the
23
22
  number of processes) that it returns is based on +number+, and sorted by
24
23
  +pctcpu+. By default, the size and field values are 10 and "pctcpu",
@@ -27,19 +26,16 @@ Top.top(number=10, field="pctcpu")
27
26
  = Notes
28
27
  Not all fields are available on all platforms. Please check your
29
28
  platform specific documentation for which fields are available.
30
-
31
- I used sort() instead of sort_by() internally to maintain backward
32
- compatability with Ruby 1.6.8, which I happen to need.
33
29
 
34
30
  = Bugs
35
31
  None that I'm aware of. Please log bug reports on the project page at
36
32
  http://www.rubyforge.org/projects/sysutils
37
33
 
38
34
  = License
39
- Ruby's
35
+ Artistic 2.0
40
36
 
41
37
  = Copyright
42
- (C) 2004-2006 Daniel J. Berger
38
+ (C) 2004-2009 Daniel J. Berger
43
39
  All Rights Reserved.
44
40
 
45
41
  = Warranty
@@ -48,6 +44,4 @@ Top.top(number=10, field="pctcpu")
48
44
  warranties of merchantability and fitness for a particular purpose.
49
45
 
50
46
  = Author
51
- Daniel J. Berger
52
- djberg96 at nospam at gmail dot com
53
- imperator on IRC (Freenode)
47
+ Daniel J. Berger
@@ -0,0 +1,20 @@
1
+ #######################################################################
2
+ # example_ps.rb
3
+ #
4
+ # Generic test program that demonstrates the use of ProcTable.ps. You
5
+ # can run this via the 'rake example' task.
6
+ #
7
+ # Modify as you see fit
8
+ #######################################################################
9
+ require 'sys/proctable'
10
+ include Sys
11
+
12
+ puts "VERSION: " + ProcTable::VERSION
13
+ sleep 2
14
+
15
+ ProcTable.ps{ |s|
16
+ ProcTable.fields.each{ |field|
17
+ puts "#{field}: " + s.send(field).to_s
18
+ }
19
+ puts '=' * 30
20
+ }
@@ -0,0 +1,458 @@
1
+ #######################################################################
2
+ # proctable.rb
3
+ #
4
+ # A pure Ruby version of sys-proctable for AIX 5.3 or later.
5
+ ########################################################################
6
+ require 'sys/proctable/version'
7
+
8
+ # The Sys module serves as a namespace only.
9
+ module Sys
10
+
11
+ # The ProcTable class encapsulates process table information.
12
+ class ProcTable
13
+
14
+ class Error < StandardError; end
15
+
16
+ # There is no constructor
17
+ private_class_method :new
18
+
19
+ private
20
+
21
+ @fields = [
22
+ # --- psinfo_t ---
23
+ :flag, # process flags from proc struct p_flag
24
+ :flag2, # process flags from proc struct p_flag2
25
+ :nlwp, # number of threads in process
26
+ #:pad1, # reserved for future use
27
+ :uid, # real user id
28
+ :euid, # effective user id
29
+ :gid, # real group id
30
+ :egid, # effective group id
31
+ :pid, # unique process id
32
+ :ppid, # process id of parent
33
+ :pgid, # pid of process group leader
34
+ :sid, # session id
35
+ :ttydev, # controlling tty device (device #)
36
+ :s_ttydev, # controlling tty device name or '-'
37
+ :addr, # internal address of proc struct
38
+ :size, # process image size in KB (1024) units
39
+ :rssize, # resident set size in KB (1024) units
40
+ :start, # process start time, time since epoch
41
+ :time, # usr+sys cpu time for this process
42
+ :cid, # corral id
43
+ #:pad2, # reserved for future use
44
+ :argc, # initial argument count
45
+ :argv, # address of initial argument vector in user process
46
+ :envp, # address of initial environment vector in user process
47
+ :fname, # last component of exec()ed pathname
48
+ :psargs, # initial characters of arg list
49
+ #:pad, # reserved for future use
50
+
51
+ # --- lwpsinfo_t ---
52
+ :lwpid, # thread id
53
+ #:addr, # internal address of thread
54
+ :wchan, # wait addr for sleeping thread
55
+ #:flag, # thread flags
56
+ :wtype, # type of thread wait
57
+ :state, # thread state
58
+ :sname, # printable thread state character
59
+ :nice, # nice for cpu usage
60
+ :pri, # priority, high value = high priority
61
+ :policy, # scheduling policy
62
+ :clname, # printable scheduling policy string
63
+ :onpro, # processor on which thread last ran
64
+ :bindpro, # processor to which thread is bound
65
+ :ptid, # pthread id
66
+ #:pad1, # reserved for future use
67
+ #:pad, # reserved for future use
68
+
69
+ # --- prmap_t ---
70
+ :map, # array of prmap_t structures
71
+
72
+ # --- lwp ---
73
+ #:lwp # array of lwp information
74
+
75
+ # other...
76
+ :fd, # array of used file descriptors
77
+ :cmd_args, # array of command line arguments
78
+ :environ, # hash of environment associated with the process
79
+ :cmdline, # joined cmd_args if present, otherwise psargs
80
+ :cwd, # current working directory
81
+ ]
82
+
83
+ @psinfo_pack_directive = [
84
+ 'L', # pr_flag
85
+ 'L', # pr_flag2
86
+ 'L', # pr_nlwp
87
+ 'L', # pr__pad1
88
+ 'Q', # pr_uid
89
+ 'Q', # pr_euid
90
+ 'Q', # pr_gid
91
+ 'Q', # pr_egid
92
+ 'Q', # pr_pid
93
+ 'Q', # pr_ppid
94
+ 'Q', # pr_pgid
95
+ 'Q', # pr_sid
96
+ 'Q', # pr_ttydev
97
+ 'Q', # pr_addr
98
+ 'Q', # pr_size
99
+ 'Q', # pr_rssize
100
+ 'QlL', # pr_start
101
+ 'QlL', # pr_time
102
+ 'S', # pr_cid
103
+ 'S', # pr__pad2
104
+ 'L', # pr_argc
105
+ 'Q', # pr_argv
106
+ 'Q', # pr_envp
107
+ 'A16', # pr_fname[PRFNSZ]
108
+ 'A80', # pr_psargs[PRARGSZ]
109
+ 'Q8', # pr__pad[8]
110
+ # --- lwpsinfo_t --- pr_lwp
111
+ 'Q', # pr_lwpid
112
+ 'Q', # pr_addr
113
+ 'Q', # pr_wchan
114
+ 'L', # pr_flag
115
+ 'C', # pr_wtype
116
+ 'c', # pr_state
117
+ 'A', # pr_sname
118
+ 'C', # pr_nice
119
+ 'l', # pr_pri
120
+ 'L', # pr_policy
121
+ 'A8', # pr_clname
122
+ 'l', # pr_onpro
123
+ 'l', # pr_bindpro
124
+ 'L', # pr_ptid
125
+ 'L', # pr__pad1
126
+ 'Q7' # pr__pad[7]
127
+ ].join
128
+
129
+ # --- prmap_t ---
130
+ @map_fields = [
131
+ :size,
132
+ :vaddr,
133
+ :mapname,
134
+ :off,
135
+ :mflags,
136
+ :s_mflags,
137
+ :pathoff,
138
+ :alias,
139
+ :gp,
140
+ #:pad,
141
+ :path,
142
+ ]
143
+
144
+ @prmap_pack_directive = [
145
+ 'Q', # pr_size
146
+ 'Q', # pr_vaddr
147
+ 'A64', # pr_mapname[PRMAPSZ]
148
+ 'Q', # pr_off
149
+ 'L', # pr_mflags
150
+ 'L', # pr_pathoff
151
+ 'Q', # pr_alias
152
+ 'Q', # pr_gp
153
+ 'Q8', # pr__pad[8]
154
+ ].join
155
+
156
+ # prmap_t pr_mflags
157
+
158
+ PR_MFLAGS =
159
+ [
160
+ [ 0x80000000, 'main' ], # MA_MAINEXEC - main executable
161
+ [ 0x40000000, 'kernel' ], # MA_KERNTEXT - kernel text
162
+ [ 0x00000004, 'read' ], # MA_READ - readable
163
+ [ 0x00000002, 'write' ], # MA_WRITE - writable
164
+ [ 0x00000001, 'exec' ], # MA_EXEC - executable
165
+ [ 0x00000008, 'shared' ], # MA_SHARED - shared memory region
166
+ [ 0x00000010, 'heap' ], # MA_BREAK - heap -- grown by brk
167
+ [ 0x00000020, 'stack' ], # MA_STACK - stack -- grows on stack faults
168
+ ]
169
+
170
+ @devs = {}
171
+
172
+ Dir['/dev/**/*'].map do |filename|
173
+ begin
174
+ rdev = File.stat(filename).rdev
175
+ rescue
176
+ next
177
+ end
178
+
179
+ @devs[rdev] = filename[5..-1] if rdev.nonzero?
180
+ end
181
+
182
+ public
183
+
184
+ ProcTableStruct = Struct.new("ProcTableStruct", *@fields) do
185
+ alias comm fname
186
+ end
187
+
188
+ ProcTableMapStruct = Struct.new("ProcTableMapStruct", *@map_fields)
189
+
190
+ # In block form, yields a ProcTableStruct for each process entry that you
191
+ # have rights to. This method returns an array of ProcTableStruct's in
192
+ # non-block form.
193
+ #
194
+ # If a +pid+ is provided, then only a single ProcTableStruct is yielded or
195
+ # returned, or nil if no process information is found for that +pid+.
196
+ #
197
+ # Example:
198
+ #
199
+ # # Iterate over all processes
200
+ # ProcTable.ps do |proc_info|
201
+ # p proc_info
202
+ # end
203
+ #
204
+ # # Print process table information for only pid 1001
205
+ # p ProcTable.ps(pid: 1001)
206
+ #
207
+ def self.ps(**kwargs)
208
+ pid = kwargs[:pid]
209
+
210
+ raise TypeError unless pid.is_a?(Numeric) if pid
211
+
212
+ array = block_given? ? nil : []
213
+ struct = nil
214
+
215
+ Dir.foreach("/proc") do |file|
216
+ next if file =~ /\D/ # Skip non-numeric entries under /proc
217
+
218
+ # Only return information for a given pid, if provided
219
+ if pid
220
+ next unless file.to_i == pid
221
+ end
222
+
223
+ # Skip over any entries we don't have permissions to read
224
+ next unless File.readable?("/proc/#{file}/psinfo")
225
+
226
+ psinfo = IO.read("/proc/#{file}/psinfo") rescue next
227
+
228
+ psinfo_array = psinfo.unpack(@psinfo_pack_directive)
229
+
230
+ struct = ProcTableStruct.new
231
+
232
+ struct.flag = psinfo_array[0] # pr_flag
233
+ struct.flag2 = psinfo_array[1] # pr_flag2
234
+ struct.nlwp = psinfo_array[2] # pr_nlwp
235
+ # pr__pad1
236
+ struct.uid = psinfo_array[4] # pr_uid
237
+ struct.euid = psinfo_array[5] # pr_euid
238
+ struct.gid = psinfo_array[6] # pr_gid
239
+ struct.egid = psinfo_array[7] # pr_egid
240
+ struct.pid = psinfo_array[8] # pr_pid
241
+ struct.ppid = psinfo_array[9] # pr_ppid
242
+ struct.pgid = psinfo_array[10] # pr_pgid
243
+ struct.sid = psinfo_array[11] # pr_sid
244
+ struct.ttydev = psinfo_array[12] # pr_ttydev
245
+
246
+ # convert from 64-bit dev_t to 32-bit dev_t and then map the device
247
+ # number to a name
248
+ ttydev = struct.ttydev
249
+ ttydev = (((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF))
250
+ struct.s_ttydev = @devs.has_key?(ttydev) ? @devs[ttydev] : '-'
251
+
252
+ struct.addr = psinfo_array[13] # pr_addr
253
+ struct.size = psinfo_array[14] * 1024 # pr_size
254
+ struct.rssize = psinfo_array[15] * 1024 # pr_rssize
255
+ struct.start = Time.at(psinfo_array[16], psinfo_array[17]) # pr_start
256
+ # skip pr_start.__pad
257
+ struct.time = psinfo_array[19] # pr_time
258
+ # skip pr_time.tv_nsec and pr_time.__pad
259
+ struct.cid = psinfo_array[22] # pr_cid
260
+ # skip pr__pad2
261
+ struct.argc = psinfo_array[24] # pr_argc
262
+ struct.argv = psinfo_array[25] # pr_argv
263
+ struct.envp = psinfo_array[26] # pr_envp
264
+ struct.fname = psinfo_array[27] # pr_fname
265
+ struct.psargs = psinfo_array[28] # pr_psargs
266
+ # skip pr__pad
267
+
268
+ ### lwpsinfo_t info
269
+
270
+ struct.lwpid = psinfo_array[37] # pr_lwpid
271
+ # skip pr_addr
272
+ struct.wchan = psinfo_array[39] # pr_wchan
273
+ # skip pr_flag
274
+ struct.wtype = psinfo_array[41] # pr_wtype
275
+ struct.state = psinfo_array[42] # pr_state
276
+ struct.sname = psinfo_array[43] # pr_sname
277
+ struct.nice = psinfo_array[44] # pr_nice
278
+ struct.pri = psinfo_array[45] # pr_pri
279
+ struct.policy = psinfo_array[46] # pr_policy
280
+ struct.clname = psinfo_array[47] # pr_clname
281
+ struct.onpro = psinfo_array[48] # pr_onpro
282
+ struct.bindpro = psinfo_array[49] # pr_bindpro
283
+ struct.ptid = psinfo_array[50] # pr_ptid
284
+ # skip pr__pad1
285
+ # skip pr__pad
286
+
287
+ # Get the full command line out of /proc/<pid>/as.
288
+ begin
289
+ File.open("/proc/#{file}/as", 'rb') do |fd|
290
+ np = fd.sysseek(struct.argv, IO::SEEK_SET)
291
+
292
+ if np != struct.argv
293
+ raise Error, "argv seek to #{struct.argv}, result #{np}", caller
294
+ end
295
+
296
+ argv = fd.sysread(4).unpack('L')[0]
297
+
298
+ np = fd.sysseek(argv, IO::SEEK_SET)
299
+
300
+ if np != argv
301
+ raise Error, "*argv seek to #{argv}, result #{np}", caller
302
+ end
303
+
304
+ argv = fd.sysread(4 * struct.argc).unpack("L#{struct.argc}")
305
+
306
+ struct.cmd_args = []
307
+
308
+ argv.each_with_index do |address, i|
309
+ np = fd.sysseek(address, IO::SEEK_SET)
310
+
311
+ if np != address
312
+ raise Error, "argv[#{i}] seek to #{address}, result #{np}",
313
+ caller
314
+ end
315
+
316
+ data = fd.sysread(512)[/^[^\0]*/] # Null strip
317
+ struct.cmd_args << data
318
+ end
319
+
320
+ # Get the environment hash associated with the process.
321
+ struct.environ = {}
322
+
323
+ # First have to go to the address given by struct.envp. That will
324
+ # give us the address of the environment pointer array.
325
+
326
+ np = fd.sysseek(struct.envp, IO::SEEK_SET)
327
+
328
+ if np != struct.envp
329
+ raise Error, "envp seek to #{struct.envp}, result #{np}", caller
330
+ end
331
+
332
+ envloc = fd.sysread(4).unpack('L')[0]
333
+ n = 0
334
+
335
+ loop do
336
+ np = fd.sysseek(envloc, IO::SEEK_SET)
337
+
338
+ if np != envloc
339
+ raise Error, "envp[#{n}] seek to #{envloc}, result #{np}",
340
+ caller
341
+ end
342
+
343
+ envp = fd.sysread(4).unpack("L")[0]
344
+ break if envp.zero?
345
+ np = fd.sysseek(envp, IO::SEEK_SET)
346
+ data = fd.sysread(1024)[/^[^\0]*/] # Null strip
347
+ key, value = data.split('=')
348
+ struct.environ[key] = value
349
+ envloc += 4
350
+ n += 1
351
+ end
352
+ end
353
+ rescue Errno::EACCES, Errno::EOVERFLOW, EOFError
354
+ # Skip this if we don't have proper permissions, if there's
355
+ # no associated environment, or if there's a largefile issue.
356
+ rescue Errno::ENOENT
357
+ next # The process has terminated. Bail out!
358
+ end
359
+
360
+ # Information from /proc/<pid>/fd. This returns an array of
361
+ # numeric file descriptors used by the process.
362
+ struct.fd = Dir["/proc/#{file}/fd/*"].map { |f| File.basename(f).to_i }
363
+
364
+ # Use the cmd_args as the cmdline if available. Otherwise use
365
+ # the psargs. This struct member is provided to provide a measure
366
+ # of consistency with the other platform implementations.
367
+ if struct.cmd_args.nil? || struct.cmd_args.empty?
368
+ struct.cmdline = struct.psargs
369
+ else
370
+ struct.cmdline = struct.cmd_args.join(' ')
371
+ end
372
+
373
+ # get current working directory from /proc/<pid>/cwd
374
+ struct.cwd = File.readlink("/proc/#{file}/cwd") rescue nil
375
+
376
+ # get virtual address map from /proc/<pid>/map
377
+ begin
378
+ struct.map = []
379
+
380
+ File.open("/proc/#{file}/map", 'rb') do |fd|
381
+ loop do
382
+ prmap_array = fd.sysread(176).unpack(@prmap_pack_directive)
383
+ break if prmap_array[0].zero?
384
+
385
+ map_struct = ProcTableMapStruct.new
386
+
387
+ map_struct.size = prmap_array[0] # pr_size
388
+ map_struct.vaddr = prmap_array[1] # pr_vaddr
389
+ map_struct.mapname = prmap_array[2] # pr_mapname
390
+ map_struct.off = prmap_array[3] # pr_off
391
+ map_struct.mflags = prmap_array[4] # pr_mflags
392
+
393
+ # convert pr_mflags value to string sort of like procmap outputs
394
+ mflags = map_struct.mflags
395
+ map_struct.s_mflags = ''
396
+ sep = ''
397
+
398
+ PR_MFLAGS.each do |flag|
399
+ if (mflags & flag[0]).nonzero?
400
+ map_struct.s_mflags << sep << flag[1]
401
+ sep = '/'
402
+ mflags &= ~flag[0]
403
+ end
404
+ end
405
+
406
+ if mflags.nonzero?
407
+ map_struct.s_mflags << sep << sprintf('%08x', mflags)
408
+ end
409
+
410
+ map_struct.pathoff = prmap_array[5] # pr_pathoff
411
+ map_struct.alias = prmap_array[6] # pr_alias
412
+ map_struct.gp = prmap_array[7] # pr_gp
413
+
414
+ struct.map << map_struct
415
+ end
416
+
417
+ struct.map.each do |m|
418
+ next if m.pathoff.zero?
419
+ fd.sysseek(m.pathoff, IO::SEEK_SET)
420
+ buf = fd.sysread(4096)
421
+ buf =~ /^([^\0]*)\0([^\0]*)\0/
422
+ m.path = $2.empty? ? $1 : "#{$1}(#{$2})"
423
+ end
424
+ end
425
+
426
+ struct.map = nil if struct.map.empty?
427
+ rescue
428
+ struct.map = nil
429
+ end
430
+
431
+ # This is read-only data
432
+ struct.freeze
433
+
434
+ if block_given?
435
+ yield struct
436
+ else
437
+ array << struct
438
+ end
439
+ end
440
+
441
+ pid ? struct : array
442
+ end
443
+
444
+ # Returns an array of fields that each ProcTableStruct will contain. This
445
+ # may be useful if you want to know in advance what fields are available
446
+ # without having to perform at least one read of the /proc table.
447
+ #
448
+ # Example:
449
+ #
450
+ # Sys::ProcTable.fields.each do |field|
451
+ # puts "Field: #{field}"
452
+ # end
453
+ #
454
+ def self.fields
455
+ @fields.map{ |f| f.to_s }
456
+ end
457
+ end
458
+ end