sys-proctable 0.7.6 → 1.2.0

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