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,456 @@
1
+ ########################################################################
2
+ # proctable.rb
3
+ #
4
+ # A pure Ruby version of sys-proctable for SunOS 5.8 or later.
5
+ ########################################################################
6
+ require 'ffi'
7
+ require 'sys/proctable/version'
8
+
9
+ # The Sys module serves as a namespace only.
10
+ module Sys
11
+
12
+ # The ProcTable class encapsulates process table information.
13
+ class ProcTable
14
+ extend FFI::Library
15
+
16
+ class Error < StandardError; end
17
+
18
+ # There is no constructor
19
+ private_class_method :new
20
+
21
+ private
22
+
23
+ class Timeval < FFI::Struct
24
+ layout(:tv_sec, :time_t, :tv_usec, :time_t)
25
+ end
26
+
27
+ class LWPSInfo < FFI::Struct
28
+ layout(
29
+ :pr_flag, :int,
30
+ :pr_lwpid, :id_t,
31
+ :pr_addr, :uintptr_t,
32
+ :pr_wchan, :uintptr_t,
33
+ :pr_stype, :char,
34
+ :pr_state, :char,
35
+ :pr_sname, :char,
36
+ :pr_nice, :char,
37
+ :pr_syscall, :short,
38
+ :pr_oldpri, :char,
39
+ :pr_cpu, :char,
40
+ :pr_pri, :int,
41
+ :pr_pctcpu, :ushort_t,
42
+ :pr_pad, :ushort_t,
43
+ :pr_start, Timeval,
44
+ :pr_time, Timeval,
45
+ :pr_clname, [:char, 8],
46
+ :pr_name, [:char, 16],
47
+ :pr_onpro, :int,
48
+ :pr_bindpro, :int,
49
+ :pr_bindpset, :int,
50
+ :pr_filler, [:int, 5]
51
+ )
52
+ end
53
+
54
+ class PSInfo < FFI::Struct
55
+ layout(
56
+ :pr_flag, :int,
57
+ :pr_nlwp, :int,
58
+ :pr_pid, :pid_t,
59
+ :pr_ppid, :pid_t,
60
+ :pr_pgid, :pid_t,
61
+ :pr_sid, :pid_t,
62
+ :pr_uid, :uid_t,
63
+ :pr_euid, :uid_t,
64
+ :pr_gid, :gid_t,
65
+ :pr_egid, :gid_t,
66
+ :pr_addr, :uintptr_t,
67
+ :pr_size, :size_t,
68
+ :pr_rssize, :size_t,
69
+ :pr_pad1, :size_t,
70
+ :pr_ttydev, :dev_t,
71
+ :pr_pctcpu, :ushort_t,
72
+ :pr_pctmem, :ushort_t,
73
+ :pr_start, Timeval,
74
+ :pr_time, Timeval,
75
+ :pr_ctime, Timeval,
76
+ :pr_fname, [:char, 16],
77
+ :pr_psargs, [:char, 80],
78
+ :pr_wstat, :int,
79
+ :pr_argc, :int,
80
+ :pr_argv, :uintptr_t,
81
+ :pr_envp, :uintptr_t,
82
+ :pr_dmodel, :char,
83
+ :pr_pad2, [:char, 3],
84
+ :pr_taskid, :taskid_t,
85
+ :pr_projid, :projid_t,
86
+ :pr_nzomb, :int,
87
+ :pr_poolid, :poolid_t,
88
+ :pr_zoneid, :zoneid_t,
89
+ :pr_contract, :id_t,
90
+ :pr_filler, [:int, 1],
91
+ :pr_lwp, LWPSInfo
92
+ )
93
+ end
94
+
95
+ class PRUsage < FFI::Struct
96
+ layout(
97
+ :pr_lwpid, :id_t,
98
+ :pr_count, :int,
99
+ :pr_tstamp, Timeval,
100
+ :pr_create, Timeval,
101
+ :pr_term, Timeval,
102
+ :pr_rtime, Timeval,
103
+ :pr_utime, Timeval,
104
+ :pr_stime, Timeval,
105
+ :pr_ttime, Timeval,
106
+ :pr_tftime, Timeval,
107
+ :pr_dftime, Timeval,
108
+ :pr_kftime, Timeval,
109
+ :pr_ltime, Timeval,
110
+ :pr_slptime, Timeval,
111
+ :pr_wtime, Timeval,
112
+ :pr_stoptime, Timeval,
113
+ :pr_filetime, [Timeval,6],
114
+ :pr_minf, :ulong_t,
115
+ :pr_majf, :ulong_t,
116
+ :pr_nswap, :ulong_t,
117
+ :pr_inblk, :ulong_t,
118
+ :pr_oublk, :ulong_t,
119
+ :pr_msnd, :ulong_t,
120
+ :pr_mrcv, :ulong_t,
121
+ :pr_sigs, :ulong_t,
122
+ :pr_vctx, :ulong_t,
123
+ :pr_ictx, :ulong_t,
124
+ :pr_sysc, :ulong_t,
125
+ :pr_ioch, :ulong_t,
126
+ :filler, [:ulong_t, 10]
127
+ )
128
+ end
129
+
130
+ PRNODEV = (1<<FFI::Platform::ADDRESS_SIZE)-1
131
+
132
+ @fields = [
133
+ :flag, # process flags (deprecated)
134
+ :nlwp, # number of active lwp's in the process
135
+ :pid, # unique process id
136
+ :ppid, # process id of parent
137
+ :pgid, # pid of session leader
138
+ :sid, # session id
139
+ :uid, # real user id
140
+ :euid, # effective user id
141
+ :gid, # real group id
142
+ :egid, # effective group id
143
+ :addr, # address of the process
144
+ :size, # size of process in kbytes
145
+ :rssize, # resident set size in kbytes
146
+ :ttydev, # tty device (or PRNODEV)
147
+ :pctcpu, # % of recent cpu used by all lwp's
148
+ :pctmem, # % of system memory used by process
149
+ :start, # absolute process start time
150
+ :time, # usr + sys cpu time for this process
151
+ :ctime, # usr + sys cpu time for reaped children
152
+ :fname, # name of the exec'd file
153
+ :psargs, # initial characters argument list - same as cmdline
154
+ :wstat, # if a zombie, the wait status
155
+ :argc, # initial argument count
156
+ :argv, # address of initial argument vector
157
+ :envp, # address of initial environment vector
158
+ :dmodel, # data model of the process
159
+ :taskid, # task id
160
+ :projid, # project id
161
+ :nzomb, # number of zombie lwp's in the process
162
+ :poolid, # pool id
163
+ :zoneid, # zone id
164
+ :contract, # process contract
165
+ :lwpid, # lwp id
166
+ :wchan, # wait address for sleeping lwp
167
+ :stype, # synchronization event type
168
+ :state, # numeric lwp state
169
+ :sname, # printable character for state
170
+ :nice, # nice for cpu usage
171
+ :syscall, # system call number (if in syscall)
172
+ :pri, # priority
173
+ :clname, # scheduling class name
174
+ :name, # name of system lwp
175
+ :onpro, # processor which last ran thsi lwp
176
+ :bindpro, # processor to which lwp is bound
177
+ :bindpset, # processor set to which lwp is bound
178
+ :count, # number of contributing lwp's
179
+ :tstamp, # current time stamp
180
+ :create, # process/lwp creation time stamp
181
+ :term, # process/lwp termination time stamp
182
+ :rtime, # total lwp real (elapsed) time
183
+ :utime, # user level cpu time
184
+ :stime, # system call cpu time
185
+ :ttime, # other system trap cpu time
186
+ :tftime, # text page fault sleep time
187
+ :dftime, # text page fault sleep time
188
+ :kftime, # kernel page fault sleep time
189
+ :ltime, # user lock wait sleep time
190
+ :slptime, # all other sleep time
191
+ :wtime, # wait-cpu (latency) time
192
+ :stoptime, # stopped time
193
+ :minf, # minor page faults
194
+ :majf, # major page faults
195
+ :nswap, # swaps
196
+ :inblk, # input blocks
197
+ :oublk, # output blocks
198
+ :msnd, # messages sent
199
+ :mrcv, # messages received
200
+ :sigs, # signals received
201
+ :vctx, # voluntary context switches
202
+ :ictx, # involuntary context switches
203
+ :sysc, # system calls
204
+ :ioch, # chars read and written
205
+ :path, # array of symbolic link paths from /proc/<pid>/path
206
+ :contracts, # array symbolic link paths from /proc/<pid>/contracts
207
+ :fd, # array of used file descriptors
208
+ :cmd_args, # array of command line arguments
209
+ :environ, # hash of environment associated with the process,
210
+ :cmdline # joined cmd_args if present, otherwise psargs
211
+ ]
212
+
213
+ public
214
+
215
+ ProcTableStruct = Struct.new("ProcTableStruct", *@fields) do
216
+ alias comm fname
217
+ end
218
+
219
+ # In block form, yields a ProcTableStruct for each process entry that you
220
+ # have rights to. This method returns an array of ProcTableStruct's in
221
+ # non-block form.
222
+ #
223
+ # If a +pid+ is provided, then only a single ProcTableStruct is yielded or
224
+ # returned, or nil if no process information is found for that +pid+.
225
+ #
226
+ # Example:
227
+ #
228
+ # # Iterate over all processes
229
+ # ProcTable.ps do |proc_info|
230
+ # p proc_info
231
+ # end
232
+ #
233
+ # # Print process table information for only pid 1001
234
+ # p ProcTable.ps(pid: 1001)
235
+ #
236
+ # # Skip prusage information
237
+ # p ProcTable.ps(prusage: false)
238
+ #
239
+ def self.ps(**kwargs)
240
+ pid = kwargs[:pid]
241
+ prusage_info = kwargs[:prusage]
242
+
243
+ raise TypeError unless pid.is_a?(Numeric) if pid
244
+
245
+ array = block_given? ? nil : []
246
+ struct = nil
247
+
248
+ Dir.foreach("/proc") do |file|
249
+ next if file =~ /\D/ # Skip non-numeric entries under /proc
250
+
251
+ # Only return information for a given pid, if provided
252
+ next unless file.to_i == pid if pid
253
+
254
+ # Skip over any entries we don't have permissions to read
255
+ next unless File.readable?("/proc/#{file}/psinfo")
256
+
257
+ data = IO.read("/proc/#{file}/psinfo") rescue next
258
+ psinfo = PSInfo.new(FFI::MemoryPointer.from_string(data))
259
+
260
+ struct = ProcTableStruct.new
261
+
262
+ struct.flag = psinfo[:pr_flag]
263
+ struct.nlwp = psinfo[:pr_nlwp]
264
+ struct.pid = psinfo[:pr_pid]
265
+ struct.ppid = psinfo[:pr_ppid]
266
+ struct.pgid = psinfo[:pr_pgid]
267
+ struct.sid = psinfo[:pr_sid]
268
+ struct.uid = psinfo[:pr_uid]
269
+ struct.euid = psinfo[:pr_euid]
270
+ struct.gid = psinfo[:pr_gid]
271
+ struct.egid = psinfo[:pr_egid]
272
+ struct.addr = psinfo[:pr_addr]
273
+ struct.size = psinfo[:pr_size] * 1024 # bytes
274
+ struct.rssize = psinfo[:pr_rssize] * 1024 # bytes
275
+ struct.ttydev = psinfo[:pr_ttydev] == PRNODEV ? -1 : psinfo[:pr_ttydev]
276
+ struct.pctcpu = (psinfo[:pr_pctcpu] * 100).to_f / 0x8000
277
+ struct.pctmem = (psinfo[:pr_pctmem] * 100).to_f / 0x8000
278
+
279
+ struct.start = Time.at(psinfo[:pr_start][:tv_sec])
280
+ struct.time = psinfo[:pr_time][:tv_sec]
281
+ struct.ctime = psinfo[:pr_ctime][:tv_sec]
282
+
283
+ struct.fname = psinfo[:pr_fname].to_s
284
+ struct.psargs = psinfo[:pr_psargs].to_s
285
+ struct.wstat = psinfo[:pr_wstat]
286
+ struct.argc = psinfo[:pr_argc]
287
+ struct.argv = psinfo[:pr_argv]
288
+ struct.envp = psinfo[:pr_envp]
289
+ struct.dmodel = psinfo[:pr_dmodel]
290
+
291
+ struct.taskid = psinfo[:pr_taskid]
292
+ struct.projid = psinfo[:pr_projid]
293
+ struct.nzomb = psinfo[:pr_nzomb]
294
+ struct.poolid = psinfo[:pr_poolid]
295
+ struct.zoneid = psinfo[:pr_zoneid]
296
+ struct.contract = psinfo[:pr_contract]
297
+
298
+ ### LWPSINFO struct info
299
+
300
+ struct.lwpid = psinfo[:pr_lwp][:pr_lwpid]
301
+ struct.wchan = psinfo[:pr_lwp][:pr_wchan]
302
+ struct.stype = psinfo[:pr_lwp][:pr_stype]
303
+ struct.state = psinfo[:pr_lwp][:pr_state]
304
+ struct.sname = psinfo[:pr_lwp][:pr_sname].chr
305
+ struct.nice = psinfo[:pr_lwp][:pr_nice]
306
+ struct.syscall = psinfo[:pr_lwp][:pr_syscall]
307
+ struct.pri = psinfo[:pr_lwp][:pr_pri]
308
+ struct.clname = psinfo[:pr_lwp][:pr_clname].to_s
309
+ struct.name = psinfo[:pr_lwp][:pr_name].to_s
310
+ struct.onpro = psinfo[:pr_lwp][:pr_onpro]
311
+ struct.bindpro = psinfo[:pr_lwp][:pr_bindpro]
312
+ struct.bindpset = psinfo[:pr_lwp][:pr_bindpset]
313
+
314
+ # Get the full command line out of /proc/<pid>/as.
315
+ begin
316
+ File.open("/proc/#{file}/as") do |fd|
317
+ fd.sysseek(struct.argv, IO::SEEK_SET)
318
+ address = fd.sysread(struct.argc * 4).unpack("L")[0]
319
+
320
+ struct.cmd_args = []
321
+
322
+ 0.upto(struct.argc - 1){ |i|
323
+ fd.sysseek(address, IO::SEEK_SET)
324
+ data = fd.sysread(128)[/^[^\0]*/] # Null strip
325
+ struct.cmd_args << data
326
+ address += data.length + 1 # Add 1 for the space
327
+ }
328
+
329
+ # Get the environment hash associated with the process.
330
+ struct.environ = {}
331
+
332
+ fd.sysseek(struct.envp, IO::SEEK_SET)
333
+
334
+ env_address = fd.sysread(128).unpack("L")[0]
335
+
336
+ # TODO: Optimization potential here.
337
+ loop do
338
+ fd.sysseek(env_address, IO::SEEK_SET)
339
+ data = fd.sysread(1024)[/^[^\0]*/] # Null strip
340
+ break if data.empty?
341
+ key, value = data.split('=')
342
+ struct.environ[key] = value
343
+ env_address += data.length + 1 # Add 1 for the space
344
+ end
345
+ end
346
+ rescue Errno::EACCES, Errno::EOVERFLOW, EOFError, RangeError
347
+ # Skip this if we don't have proper permissions, if there's
348
+ # no associated environment, or if there's a largefile issue.
349
+ rescue Errno::ENOENT
350
+ next # The process has terminated. Bail out!
351
+ end
352
+
353
+ ### struct prusage
354
+
355
+ if prusage_info != false
356
+ begin
357
+ data = IO.read("/proc/#{file}/usage")
358
+ prusage = PRUsage.new(FFI::MemoryPointer.from_string(data))
359
+
360
+ struct.count = prusage[:pr_count]
361
+ struct.tstamp = prusage[:pr_tstamp][:tv_sec]
362
+ struct.create = prusage[:pr_create][:tv_sec]
363
+ struct.term = prusage[:pr_term][:tv_sec]
364
+ struct.rtime = prusage[:pr_rtime][:tv_sec]
365
+ struct.utime = prusage[:pr_utime][:tv_sec]
366
+ struct.stime = prusage[:pr_stime][:tv_sec]
367
+ struct.ttime = prusage[:pr_ttime][:tv_sec]
368
+ struct.tftime = prusage[:pr_tftime][:tv_sec]
369
+ struct.dftime = prusage[:pr_dftime][:tv_sec]
370
+ struct.kftime = prusage[:pr_kftime][:tv_sec]
371
+ struct.ltime = prusage[:pr_ltime][:tv_sec]
372
+ struct.slptime = prusage[:pr_slptime][:tv_sec]
373
+ struct.wtime = prusage[:pr_wtime][:tv_sec]
374
+ struct.stoptime = prusage[:pr_stoptime][:tv_sec]
375
+ struct.minf = prusage[:pr_minf]
376
+ struct.majf = prusage[:pr_majf]
377
+ struct.nswap = prusage[:pr_nswap]
378
+ struct.inblk = prusage[:pr_inblk]
379
+ struct.oublk = prusage[:pr_oublk]
380
+ struct.msnd = prusage[:pr_msnd]
381
+ struct.mrcv = prusage[:pr_mrcv]
382
+ struct.sigs = prusage[:pr_sigs]
383
+ struct.vctx = prusage[:pr_vctx]
384
+ struct.ictx = prusage[:pr_ictx]
385
+ struct.sysc = prusage[:pr_sysc]
386
+ struct.ioch = prusage[:pr_ioch]
387
+ rescue Errno::EACCES
388
+ # Do nothing if we lack permissions. Just move on.
389
+ rescue Errno::ENOENT
390
+ next # The process has terminated. Bail out!
391
+ end
392
+ end
393
+
394
+ # Information from /proc/<pid>/path. This is represented as a hash,
395
+ # with the symbolic link name as the key, and the file it links to
396
+ # as the value, or nil if it cannot be found.
397
+ #--
398
+ # Note that cwd information can be gathered from here, too.
399
+ struct.path = {}
400
+
401
+ Dir["/proc/#{file}/path/*"].each{ |entry|
402
+ link = File.readlink(entry) rescue nil
403
+ struct.path[File.basename(entry)] = link
404
+ }
405
+
406
+ # Information from /proc/<pid>/contracts. This is represented as
407
+ # a hash, with the symbolic link name as the key, and the file
408
+ # it links to as the value.
409
+ struct.contracts = {}
410
+
411
+ Dir["/proc/#{file}/contracts/*"].each{ |entry|
412
+ link = File.readlink(entry) rescue nil
413
+ struct.contracts[File.basename(entry)] = link
414
+ }
415
+
416
+ # Information from /proc/<pid>/fd. This returns an array of
417
+ # numeric file descriptors used by the process.
418
+ struct.fd = Dir["/proc/#{file}/fd/*"].map{ |f| File.basename(f).to_i }
419
+
420
+ # Use the cmd_args as the cmdline if available. Otherwise use
421
+ # the psargs. This struct member is provided to provide a measure
422
+ # of consistency with the other platform implementations.
423
+ if struct.cmd_args && struct.cmd_args.length > 0
424
+ struct.cmdline = struct.cmd_args.join(' ')
425
+ else
426
+ struct.cmdline = struct.psargs
427
+ end
428
+
429
+ # This is read-only data
430
+ struct.freeze
431
+
432
+ if block_given?
433
+ yield struct
434
+ else
435
+ array << struct
436
+ end
437
+ end
438
+
439
+ pid ? struct : array
440
+ end
441
+
442
+ # Returns an array of fields that each ProcTableStruct will contain. This
443
+ # may be useful if you want to know in advance what fields are available
444
+ # without having to perform at least one read of the /proc table.
445
+ #
446
+ # Example:
447
+ #
448
+ # Sys::ProcTable.fields.each{ |field|
449
+ # puts "Field: #{field}"
450
+ # }
451
+ #
452
+ def self.fields
453
+ @fields.map(&:to_s)
454
+ end
455
+ end
456
+ end