tddium-sys-proctable 0.9.2-x86-linux
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.
- data/CHANGES +294 -0
- data/MANIFEST +30 -0
- data/README +119 -0
- data/Rakefile +158 -0
- data/benchmarks/bench_ps.rb +21 -0
- data/doc/top.txt +47 -0
- data/examples/example_ps.rb +20 -0
- data/lib/linux/sys/proctable.rb +281 -0
- data/lib/sys/top.rb +32 -0
- data/sys-proctable.gemspec +42 -0
- data/test/test_sys_proctable_all.rb +85 -0
- data/test/test_sys_proctable_linux.rb +298 -0
- metadata +82 -0
@@ -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
|
data/doc/top.txt
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
= Description
|
2
|
+
A simple 'top' interface for Ruby
|
3
|
+
|
4
|
+
= Prerequisites
|
5
|
+
Requires the "sys/proctable" package (which should be installed along
|
6
|
+
with this package).
|
7
|
+
|
8
|
+
= Synopsis
|
9
|
+
require "sys/top"
|
10
|
+
|
11
|
+
Sys::Top.top(5).each{ |ps|
|
12
|
+
p ps
|
13
|
+
}
|
14
|
+
|
15
|
+
= Constants
|
16
|
+
VERSION
|
17
|
+
Returns the version number of this package as a String.
|
18
|
+
|
19
|
+
= Class Methods
|
20
|
+
Sys::Top.top(number=10, field="pctcpu")
|
21
|
+
Returns an array of ProcTableStruct's. The size of the array (i.e. the
|
22
|
+
number of processes) that it returns is based on +number+, and sorted by
|
23
|
+
+pctcpu+. By default, the size and field values are 10 and "pctcpu",
|
24
|
+
respectively.
|
25
|
+
|
26
|
+
= Notes
|
27
|
+
Not all fields are available on all platforms. Please check your
|
28
|
+
platform specific documentation for which fields are available.
|
29
|
+
|
30
|
+
= Bugs
|
31
|
+
None that I'm aware of. Please log bug reports on the project page at
|
32
|
+
http://www.rubyforge.org/projects/sysutils
|
33
|
+
|
34
|
+
= License
|
35
|
+
Artistic 2.0
|
36
|
+
|
37
|
+
= Copyright
|
38
|
+
(C) 2004-2009 Daniel J. Berger
|
39
|
+
All Rights Reserved.
|
40
|
+
|
41
|
+
= Warranty
|
42
|
+
This package is provided "as is" and without any express or
|
43
|
+
implied warranties, including, without limitation, the implied
|
44
|
+
warranties of merchantability and fitness for a particular purpose.
|
45
|
+
|
46
|
+
= Author
|
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,281 @@
|
|
1
|
+
# The Sys module serves as a namespace only.
|
2
|
+
module Sys
|
3
|
+
|
4
|
+
# The ProcTable class encapsulates process table information.
|
5
|
+
class ProcTable
|
6
|
+
|
7
|
+
# Error typically raised if the ProcTable.ps method fails.
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
# There is no constructor
|
11
|
+
private_class_method :new
|
12
|
+
|
13
|
+
# The version of the sys-proctable library
|
14
|
+
VERSION = '0.9.1'
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
@mem_total = IO.read("/proc/meminfo")[/MemTotal.*/].split[1].to_i * 1024 rescue nil
|
19
|
+
@boot_time = IO.read("/proc/stat")[/btime.*/].split.last.to_i rescue nil
|
20
|
+
|
21
|
+
@fields = [
|
22
|
+
'cmdline', # Complete command line
|
23
|
+
'cwd', # Current working directory
|
24
|
+
'environ', # Environment
|
25
|
+
'exe', # Actual pathname of the executed command
|
26
|
+
'fd', # File descriptors open by process
|
27
|
+
'root', # Root directory of process
|
28
|
+
'pid', # Process ID
|
29
|
+
'comm', # Filename of executable
|
30
|
+
'state', # Single character state abbreviation
|
31
|
+
'ppid', # Parent process ID
|
32
|
+
'pgrp', # Process group
|
33
|
+
'session', # Session ID
|
34
|
+
'tty_nr', # TTY (terminal) associated with the process
|
35
|
+
'tpgid', # Group ID of the TTY
|
36
|
+
'flags', # Kernel flags
|
37
|
+
'minflt', # Number of minor faults
|
38
|
+
'cminflt', # Number of minor faults of waited-for children
|
39
|
+
'majflt', # Number of major faults
|
40
|
+
'cmajflt', # Number of major faults of waited-for children
|
41
|
+
'utime', # Number of user mode jiffies
|
42
|
+
'stime', # Number of kernel mode jiffies
|
43
|
+
'cutime', # Number of children's user mode jiffies
|
44
|
+
'cstime', # Number of children's kernel mode jiffies
|
45
|
+
'priority', # Nice value plus 15
|
46
|
+
'nice', # Nice value
|
47
|
+
'itrealvalue', # Time in jiffies before next SIGALRM
|
48
|
+
'starttime', # Time in jiffies since system boot
|
49
|
+
'vsize', # Virtual memory size in bytes
|
50
|
+
'rss', # Resident set size
|
51
|
+
'rlim', # Current limit on the rss in bytes
|
52
|
+
'startcode', # Address above which program text can run
|
53
|
+
'endcode', # Address below which program text can run
|
54
|
+
'startstack', # Address of the startstack
|
55
|
+
'kstkesp', # Kernel stack page address
|
56
|
+
'kstkeip', # Kernel instruction pointer
|
57
|
+
'signal', # Bitmap of pending signals
|
58
|
+
'blocked', # Bitmap of blocked signals
|
59
|
+
'sigignore', # Bitmap of ignored signals
|
60
|
+
'sigcatch', # Bitmap of caught signals
|
61
|
+
'wchan', # Channel in which the process is waiting
|
62
|
+
'nswap', # Number of pages swapped
|
63
|
+
'cnswap', # Cumulative nswap for child processes
|
64
|
+
'exit_signal', # Signal to be sent to parent when process dies
|
65
|
+
'processor', # CPU number last executed on
|
66
|
+
'rt_priority', # Real time scheduling priority
|
67
|
+
'policy', # Scheduling policy
|
68
|
+
'name', # Process name
|
69
|
+
'uid', # Real user ID
|
70
|
+
'euid', # Effective user ID
|
71
|
+
'gid', # Real group ID
|
72
|
+
'egid', # Effective group ID
|
73
|
+
'pctcpu', # Percent of CPU usage (custom field)
|
74
|
+
'pctmem' # Percent of Memory usage (custom field)
|
75
|
+
]
|
76
|
+
|
77
|
+
public
|
78
|
+
|
79
|
+
ProcTableStruct = Struct.new('ProcTableStruct', *@fields)
|
80
|
+
|
81
|
+
# In block form, yields a ProcTableStruct for each process entry that you
|
82
|
+
# have rights to. This method returns an array of ProcTableStruct's in
|
83
|
+
# non-block form.
|
84
|
+
#
|
85
|
+
# If a +pid+ is provided, then only a single ProcTableStruct is yielded or
|
86
|
+
# returned, or nil if no process information is found for that +pid+.
|
87
|
+
#
|
88
|
+
# Example:
|
89
|
+
#
|
90
|
+
# # Iterate over all processes
|
91
|
+
# ProcTable.ps do |proc_info|
|
92
|
+
# p proc_info
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# # Print process table information for only pid 1001
|
96
|
+
# p ProcTable.ps(1001)
|
97
|
+
#
|
98
|
+
#--
|
99
|
+
# It's possible that a process could terminate while gathering
|
100
|
+
# information for that process. When that happens, this library
|
101
|
+
# will simply skip to the next record. In short, this library will
|
102
|
+
# either return all information for a process, or none at all.
|
103
|
+
#
|
104
|
+
def self.ps(pid=nil)
|
105
|
+
array = block_given? ? nil : []
|
106
|
+
struct = nil
|
107
|
+
|
108
|
+
raise TypeError unless pid.is_a?(Fixnum) if pid
|
109
|
+
|
110
|
+
Dir.foreach("/proc"){ |file|
|
111
|
+
next if file =~ /\D/ # Skip non-numeric directories
|
112
|
+
next unless file.to_i == pid if pid
|
113
|
+
|
114
|
+
struct = ProcTableStruct.new
|
115
|
+
|
116
|
+
# Get /proc/<pid>/cmdline information. Strip out embedded nulls.
|
117
|
+
begin
|
118
|
+
data = IO.read("/proc/#{file}/cmdline").tr("\000", ' ').strip
|
119
|
+
struct.cmdline = data
|
120
|
+
rescue
|
121
|
+
next # Process terminated, on to the next process
|
122
|
+
end
|
123
|
+
|
124
|
+
# Get /proc/<pid>/cwd information
|
125
|
+
struct.cwd = File.readlink("/proc/#{file}/cwd") rescue nil
|
126
|
+
|
127
|
+
# Get /proc/<pid>/environ information. Environment information
|
128
|
+
# is represented as a Hash, with the environment variable as the
|
129
|
+
# key and its value as the hash value.
|
130
|
+
struct.environ = {}
|
131
|
+
|
132
|
+
begin
|
133
|
+
IO.read("/proc/#{file}/environ").split("\0").each{ |str|
|
134
|
+
key, value = str.split('=')
|
135
|
+
struct.environ[key] = value
|
136
|
+
}
|
137
|
+
rescue Errno::EACCES
|
138
|
+
# Ignore and move on.
|
139
|
+
end
|
140
|
+
|
141
|
+
# Get /proc/<pid>/exe information
|
142
|
+
struct.exe = File.readlink("/proc/#{file}/exe") rescue nil
|
143
|
+
|
144
|
+
# Get /proc/<pid>/fd information. File descriptor information
|
145
|
+
# is represented as a Hash, with the fd as the key, and its
|
146
|
+
# symlink as the value.
|
147
|
+
struct.fd = {}
|
148
|
+
|
149
|
+
begin
|
150
|
+
Dir["/proc/#{file}/fd/*"].each do |fd|
|
151
|
+
struct.fd[File.basename(fd)] = File.readlink(fd) rescue nil
|
152
|
+
end
|
153
|
+
rescue
|
154
|
+
# Ignore and move on
|
155
|
+
end
|
156
|
+
|
157
|
+
# Get /proc/<pid>/root information
|
158
|
+
struct.root = File.readlink("/proc/#{file}/root") rescue nil
|
159
|
+
|
160
|
+
# Get /proc/<pid>/stat information
|
161
|
+
stat = IO.read("/proc/#{file}/stat") rescue next
|
162
|
+
|
163
|
+
# Deal with spaces in comm name. Courtesy of Ara Howard.
|
164
|
+
re = %r/\([^\)]+\)/
|
165
|
+
comm = stat[re]
|
166
|
+
comm.tr!(' ', '-')
|
167
|
+
stat[re] = comm
|
168
|
+
|
169
|
+
stat = stat.split
|
170
|
+
|
171
|
+
struct.pid = stat[0].to_i
|
172
|
+
struct.comm = stat[1].tr('()','') # Remove parens
|
173
|
+
struct.state = stat[2]
|
174
|
+
struct.ppid = stat[3].to_i
|
175
|
+
struct.pgrp = stat[4].to_i
|
176
|
+
struct.session = stat[5].to_i
|
177
|
+
struct.tty_nr = stat[6].to_i
|
178
|
+
struct.tpgid = stat[7].to_i
|
179
|
+
struct.flags = stat[8].to_i
|
180
|
+
struct.minflt = stat[9].to_i
|
181
|
+
struct.cminflt = stat[10].to_i
|
182
|
+
struct.majflt = stat[11].to_i
|
183
|
+
struct.cmajflt = stat[12].to_i
|
184
|
+
struct.utime = stat[13].to_i
|
185
|
+
struct.stime = stat[14].to_i
|
186
|
+
struct.cutime = stat[15].to_i
|
187
|
+
struct.cstime = stat[16].to_i
|
188
|
+
struct.priority = stat[17].to_i
|
189
|
+
struct.nice = stat[18].to_i
|
190
|
+
# Skip 19
|
191
|
+
struct.itrealvalue = stat[20].to_i
|
192
|
+
struct.starttime = stat[21].to_i
|
193
|
+
struct.vsize = stat[22].to_i
|
194
|
+
struct.rss = stat[23].to_i
|
195
|
+
struct.rlim = stat[24].to_i
|
196
|
+
struct.startcode = stat[25].to_i
|
197
|
+
struct.endcode = stat[26].to_i
|
198
|
+
struct.startstack = stat[27].to_i
|
199
|
+
struct.kstkesp = stat[28].to_i
|
200
|
+
struct.kstkeip = stat[29].to_i
|
201
|
+
struct.signal = stat[30].to_i
|
202
|
+
struct.blocked = stat[31].to_i
|
203
|
+
struct.sigignore = stat[32].to_i
|
204
|
+
struct.sigcatch = stat[33].to_i
|
205
|
+
struct.wchan = stat[34].to_i
|
206
|
+
struct.nswap = stat[35].to_i
|
207
|
+
struct.cnswap = stat[36].to_i
|
208
|
+
struct.exit_signal = stat[37].to_i
|
209
|
+
struct.processor = stat[38].to_i
|
210
|
+
struct.rt_priority = stat[39].to_i
|
211
|
+
struct.policy = stat[40].to_i
|
212
|
+
|
213
|
+
# Get /proc/<pid>/status information (name, uid, euid, gid, egid)
|
214
|
+
IO.foreach("/proc/#{file}/status") do |line|
|
215
|
+
case line
|
216
|
+
when /Name:\s*?(\w+)/
|
217
|
+
struct.name = $1
|
218
|
+
when /Uid:\s*?(\d+)\s*?(\d+)/
|
219
|
+
struct.uid = $1.to_i
|
220
|
+
struct.euid = $2.to_i
|
221
|
+
when /Gid:\s*?(\d+)\s*?(\d+)/
|
222
|
+
struct.gid = $1.to_i
|
223
|
+
struct.egid = $2.to_i
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# If cmdline is empty use comm instead
|
228
|
+
struct.cmdline = struct.comm if struct.cmdline.empty?
|
229
|
+
|
230
|
+
# Manually calculate CPU and memory usage
|
231
|
+
struct.pctcpu = get_pctcpu(struct.utime, struct.starttime)
|
232
|
+
struct.pctmem = get_pctmem(struct.rss)
|
233
|
+
|
234
|
+
struct.freeze # This is read-only data
|
235
|
+
|
236
|
+
if block_given?
|
237
|
+
yield struct
|
238
|
+
else
|
239
|
+
array << struct
|
240
|
+
end
|
241
|
+
}
|
242
|
+
|
243
|
+
pid ? struct : array
|
244
|
+
end
|
245
|
+
|
246
|
+
# Returns an array of fields that each ProcTableStruct will contain. This
|
247
|
+
# may be useful if you want to know in advance what fields are available
|
248
|
+
# without having to perform at least one read of the /proc table.
|
249
|
+
#
|
250
|
+
# Example:
|
251
|
+
#
|
252
|
+
# Sys::ProcTable.fields.each{ |field|
|
253
|
+
# puts "Field: #{field}"
|
254
|
+
# }
|
255
|
+
#
|
256
|
+
def self.fields
|
257
|
+
@fields
|
258
|
+
end
|
259
|
+
|
260
|
+
private
|
261
|
+
|
262
|
+
# Calculate the percentage of memory usage for the given process.
|
263
|
+
#
|
264
|
+
def self.get_pctmem(rss)
|
265
|
+
return nil unless @mem_total
|
266
|
+
page_size = 4096
|
267
|
+
rss_total = rss * page_size
|
268
|
+
sprintf("%3.2f", (rss_total.to_f / @mem_total) * 100).to_f
|
269
|
+
end
|
270
|
+
|
271
|
+
# Calculate the percentage of CPU usage for the given process.
|
272
|
+
#
|
273
|
+
def self.get_pctcpu(utime, start_time)
|
274
|
+
return nil unless @boot_time
|
275
|
+
hertz = 100.0
|
276
|
+
utime = (utime * 10000).to_f
|
277
|
+
stime = (start_time.to_f / hertz) + @boot_time
|
278
|
+
sprintf("%3.2f", (utime / 10000.0) / (Time.now.to_i - stime)).to_f
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
data/lib/sys/top.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'sys/proctable'
|
2
|
+
require 'rbconfig'
|
3
|
+
|
4
|
+
# The Sys module serves as a namespace only
|
5
|
+
module Sys
|
6
|
+
|
7
|
+
# The Top class serves as a toplevel name for the 'top' method.
|
8
|
+
class Top
|
9
|
+
|
10
|
+
# The version of the sys-top library
|
11
|
+
VERSION = '1.0.3'
|
12
|
+
|
13
|
+
# Returns an array of Struct::ProcTableStruct elements containing up
|
14
|
+
# to +num+ elements, sorted by +field+. The default number of elements
|
15
|
+
# is 10, while the default field is 'pctcpu'.
|
16
|
+
#
|
17
|
+
# Exception: the default sort field is 'pid' on Linux and Windows.
|
18
|
+
#
|
19
|
+
def self.top(num=10, field='pctcpu')
|
20
|
+
field = field.to_s if field.is_a?(Symbol)
|
21
|
+
|
22
|
+
windows = /mswin|win32|windows|dos|cygwin|mingw/i
|
23
|
+
|
24
|
+
# Sort by pid on Windows by default
|
25
|
+
if Config::CONFIG['host_os'].match(windows) && field == 'pctcpu'
|
26
|
+
field = 'pid'
|
27
|
+
end
|
28
|
+
|
29
|
+
Sys::ProcTable.ps.sort_by{ |obj| obj.send(field) || '' }[0..num-1]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rbconfig'
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'tddium-sys-proctable'
|
6
|
+
spec.version = '0.9.2'
|
7
|
+
spec.author = 'Daniel J. Berger'
|
8
|
+
spec.license = 'Artistic 2.0'
|
9
|
+
spec.email = 'info@tddium.com'
|
10
|
+
spec.homepage = 'http://www.rubyforge.org/projects/sysutils'
|
11
|
+
spec.platform = Gem::Platform::CURRENT
|
12
|
+
spec.summary = 'An interface for providing process table information'
|
13
|
+
spec.has_rdoc = true
|
14
|
+
spec.test_files = ['test/test_sys_proctable_all.rb']
|
15
|
+
|
16
|
+
# Additional files for your platform are added by the 'rake gem' task.
|
17
|
+
spec.files = [
|
18
|
+
'benchmarks/bench_ps.rb',
|
19
|
+
'examples/example_ps.rb',
|
20
|
+
'lib/sys/top.rb',
|
21
|
+
'CHANGES',
|
22
|
+
'MANIFEST',
|
23
|
+
'Rakefile',
|
24
|
+
'README',
|
25
|
+
'sys-proctable.gemspec'
|
26
|
+
]
|
27
|
+
|
28
|
+
spec.rubyforge_project = 'sysutils'
|
29
|
+
spec.extra_rdoc_files = ['CHANGES', 'README', 'MANIFEST', 'doc/top.txt']
|
30
|
+
|
31
|
+
spec.add_development_dependency('test-unit', '>= 2.1.2')
|
32
|
+
|
33
|
+
spec.description = <<-EOF
|
34
|
+
Testing a separate publish to see if I can get the mac install to work with bundler.
|
35
|
+
|
36
|
+
The sys-proctable library provides an interface for gathering information
|
37
|
+
about processes on your system, i.e. the process table. Most major
|
38
|
+
platforms are supported and, while different platforms may return
|
39
|
+
different information, the external interface is identical across
|
40
|
+
platforms.
|
41
|
+
EOF
|
42
|
+
end
|