server_metrics 0.0.9.0 → 0.1.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.
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'sys/proctable'
|
2
|
+
require 'server_metrics/lib/proctable_lite' # used on linux
|
2
3
|
require 'server_metrics/system_info'
|
3
4
|
|
4
5
|
# Collects information on processes. Groups processes running under the same command, and sums up their CPU & memory usage.
|
@@ -20,6 +21,7 @@ class ServerMetrics::Processes
|
|
20
21
|
@last_run
|
21
22
|
@last_jiffies
|
22
23
|
@last_process_list
|
24
|
+
@proc_table_klass = ServerMetrics::SystemInfo.os =~ /linux/ ? SysLite::ProcTable : Sys::ProcTable # this is used in calculate_processes. On Linux, use our optimized version
|
23
25
|
end
|
24
26
|
|
25
27
|
|
@@ -63,7 +65,7 @@ class ServerMetrics::Processes
|
|
63
65
|
# the collector has to be run twice to get CPU data.
|
64
66
|
def calculate_processes
|
65
67
|
## 1. get a list of all processes
|
66
|
-
processes =
|
68
|
+
processes = @proc_table_klass.ps.map{|p| ServerMetrics::Processes::Process.new(p) } # our Process object adds a method some behavior
|
67
69
|
|
68
70
|
## 2. loop through each process and calculate the CPU time.
|
69
71
|
# The CPU values returned by ProcTable are cumulative for the life of the process, which is not what we want.
|
@@ -0,0 +1,284 @@
|
|
1
|
+
# This is a special-purpose version Sys::Proctable, optimized for collecting fewer metrics (but running faster) on Linux only
|
2
|
+
# In process.rb, we conditionally use this class when the host OS is Linux.
|
3
|
+
|
4
|
+
# The Sys module serves as a namespace only.
|
5
|
+
module SysLite
|
6
|
+
|
7
|
+
# The ProcTable class encapsulates process table information.
|
8
|
+
class ProcTable
|
9
|
+
|
10
|
+
# Error typically raised if the ProcTable.ps method fails.
|
11
|
+
class Error < StandardError; end
|
12
|
+
|
13
|
+
# There is no constructor
|
14
|
+
private_class_method :new
|
15
|
+
|
16
|
+
# The version of the sys-proctable library
|
17
|
+
VERSION = '0.9.3'
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# @mem_total = IO.read("/proc/meminfo")[/MemTotal.*/].split[1].to_i * 1024 rescue nil
|
22
|
+
# @boot_time = IO.read("/proc/stat")[/btime.*/].split.last.to_i rescue nil
|
23
|
+
|
24
|
+
@fields = [
|
25
|
+
'cmdline', # Complete command line
|
26
|
+
'cwd', # Current working directory
|
27
|
+
'environ', # Environment
|
28
|
+
'exe', # Actual pathname of the executed command
|
29
|
+
'fd', # File descriptors open by process
|
30
|
+
'root', # Root directory of process
|
31
|
+
'pid', # Process ID
|
32
|
+
'comm', # Filename of executable
|
33
|
+
'state', # Single character state abbreviation
|
34
|
+
'ppid', # Parent process ID
|
35
|
+
'pgrp', # Process group
|
36
|
+
'session', # Session ID
|
37
|
+
'tty_nr', # TTY (terminal) associated with the process
|
38
|
+
'tpgid', # Group ID of the TTY
|
39
|
+
'flags', # Kernel flags
|
40
|
+
'minflt', # Number of minor faults
|
41
|
+
'cminflt', # Number of minor faults of waited-for children
|
42
|
+
'majflt', # Number of major faults
|
43
|
+
'cmajflt', # Number of major faults of waited-for children
|
44
|
+
'utime', # Number of user mode jiffies
|
45
|
+
'stime', # Number of kernel mode jiffies
|
46
|
+
'cutime', # Number of children's user mode jiffies
|
47
|
+
'cstime', # Number of children's kernel mode jiffies
|
48
|
+
'priority', # Nice value plus 15
|
49
|
+
'nice', # Nice value
|
50
|
+
'itrealvalue', # Time in jiffies before next SIGALRM
|
51
|
+
'starttime', # Time in jiffies since system boot
|
52
|
+
'vsize', # Virtual memory size in bytes
|
53
|
+
'rss', # Resident set size
|
54
|
+
'rlim', # Current limit on the rss in bytes
|
55
|
+
'startcode', # Address above which program text can run
|
56
|
+
'endcode', # Address below which program text can run
|
57
|
+
'startstack', # Address of the startstack
|
58
|
+
'kstkesp', # Kernel stack page address
|
59
|
+
'kstkeip', # Kernel instruction pointer
|
60
|
+
'signal', # Bitmap of pending signals
|
61
|
+
'blocked', # Bitmap of blocked signals
|
62
|
+
'sigignore', # Bitmap of ignored signals
|
63
|
+
'sigcatch', # Bitmap of caught signals
|
64
|
+
'wchan', # Channel in which the process is waiting
|
65
|
+
'nswap', # Number of pages swapped
|
66
|
+
'cnswap', # Cumulative nswap for child processes
|
67
|
+
'exit_signal', # Signal to be sent to parent when process dies
|
68
|
+
'processor', # CPU number last executed on
|
69
|
+
'rt_priority', # Real time scheduling priority
|
70
|
+
'policy', # Scheduling policy
|
71
|
+
'name', # Process name
|
72
|
+
'uid', # Real user ID
|
73
|
+
'euid', # Effective user ID
|
74
|
+
'gid', # Real group ID
|
75
|
+
'egid', # Effective group ID
|
76
|
+
'pctcpu', # Percent of CPU usage (custom field)
|
77
|
+
'pctmem' # Percent of Memory usage (custom field)
|
78
|
+
]
|
79
|
+
|
80
|
+
public
|
81
|
+
|
82
|
+
ProcTableStruct = Struct.new('ProcTableStructLite', *@fields)
|
83
|
+
|
84
|
+
# In block form, yields a ProcTableStruct for each process entry that you
|
85
|
+
# have rights to. This method returns an array of ProcTableStruct's in
|
86
|
+
# non-block form.
|
87
|
+
#
|
88
|
+
# If a +pid+ is provided, then only a single ProcTableStruct is yielded or
|
89
|
+
# returned, or nil if no process information is found for that +pid+.
|
90
|
+
#
|
91
|
+
# Example:
|
92
|
+
#
|
93
|
+
# # Iterate over all processes
|
94
|
+
# ProcTable.ps do |proc_info|
|
95
|
+
# p proc_info
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# # Print process table information for only pid 1001
|
99
|
+
# p ProcTable.ps(1001)
|
100
|
+
#
|
101
|
+
#--
|
102
|
+
# It's possible that a process could terminate while gathering
|
103
|
+
# information for that process. When that happens, this library
|
104
|
+
# will simply skip to the next record. In short, this library will
|
105
|
+
# either return all information for a process, or none at all.
|
106
|
+
#
|
107
|
+
def self.ps(pid=nil)
|
108
|
+
array = block_given? ? nil : []
|
109
|
+
struct = nil
|
110
|
+
|
111
|
+
raise TypeError unless pid.is_a?(Fixnum) if pid
|
112
|
+
|
113
|
+
Dir.foreach("/proc"){ |file|
|
114
|
+
next if file =~ /\D/ # Skip non-numeric directories
|
115
|
+
next unless file.to_i == pid if pid
|
116
|
+
|
117
|
+
struct = ProcTableStruct.new
|
118
|
+
|
119
|
+
# Get /proc/<pid>/cmdline information. Strip out embedded nulls.
|
120
|
+
# begin
|
121
|
+
# data = IO.read("/proc/#{file}/cmdline").tr("\000", ' ').strip
|
122
|
+
# struct.cmdline = data
|
123
|
+
# rescue
|
124
|
+
# next # Process terminated, on to the next process
|
125
|
+
# end
|
126
|
+
|
127
|
+
# Get /proc/<pid>/cwd information
|
128
|
+
# struct.cwd = File.readlink("/proc/#{file}/cwd") rescue nil
|
129
|
+
|
130
|
+
# Get /proc/<pid>/environ information. Environment information
|
131
|
+
# is represented as a Hash, with the environment variable as the
|
132
|
+
# key and its value as the hash value.
|
133
|
+
struct.environ = {}
|
134
|
+
|
135
|
+
# begin
|
136
|
+
# IO.read("/proc/#{file}/environ").split("\0").each{ |str|
|
137
|
+
# key, value = str.split('=')
|
138
|
+
# struct.environ[key] = value
|
139
|
+
# }
|
140
|
+
# rescue Errno::EACCES, Errno::ESRCH, Errno::ENOENT
|
141
|
+
# # Ignore and move on.
|
142
|
+
# end
|
143
|
+
|
144
|
+
# Get /proc/<pid>/exe information
|
145
|
+
# struct.exe = File.readlink("/proc/#{file}/exe") rescue nil
|
146
|
+
|
147
|
+
# Get /proc/<pid>/fd information. File descriptor information
|
148
|
+
# is represented as a Hash, with the fd as the key, and its
|
149
|
+
# symlink as the value.
|
150
|
+
struct.fd = {}
|
151
|
+
|
152
|
+
# begin
|
153
|
+
# Dir["/proc/#{file}/fd/*"].each do |fd|
|
154
|
+
# struct.fd[File.basename(fd)] = File.readlink(fd) rescue nil
|
155
|
+
# end
|
156
|
+
# rescue
|
157
|
+
# # Ignore and move on
|
158
|
+
# end
|
159
|
+
|
160
|
+
# Get /proc/<pid>/root information
|
161
|
+
# struct.root = File.readlink("/proc/#{file}/root") rescue nil
|
162
|
+
|
163
|
+
# Get /proc/<pid>/stat information
|
164
|
+
stat = IO.read("/proc/#{file}/stat") rescue next
|
165
|
+
|
166
|
+
# Deal with spaces in comm name. Courtesy of Ara Howard.
|
167
|
+
re = %r/\([^\)]+\)/
|
168
|
+
comm = stat[re]
|
169
|
+
comm.tr!(' ', '-')
|
170
|
+
stat[re] = comm
|
171
|
+
|
172
|
+
stat = stat.split
|
173
|
+
|
174
|
+
struct.pid = stat[0].to_i
|
175
|
+
struct.comm = stat[1].tr('()','') # Remove parens
|
176
|
+
# struct.state = stat[2]
|
177
|
+
# struct.ppid = stat[3].to_i
|
178
|
+
# struct.pgrp = stat[4].to_i
|
179
|
+
# struct.session = stat[5].to_i
|
180
|
+
# struct.tty_nr = stat[6].to_i
|
181
|
+
# struct.tpgid = stat[7].to_i
|
182
|
+
# struct.flags = stat[8].to_i
|
183
|
+
# struct.minflt = stat[9].to_i
|
184
|
+
# struct.cminflt = stat[10].to_i
|
185
|
+
# struct.majflt = stat[11].to_i
|
186
|
+
# struct.cmajflt = stat[12].to_i
|
187
|
+
struct.utime = stat[13].to_i
|
188
|
+
struct.stime = stat[14].to_i
|
189
|
+
# struct.cutime = stat[15].to_i
|
190
|
+
# struct.cstime = stat[16].to_i
|
191
|
+
# struct.priority = stat[17].to_i
|
192
|
+
# struct.nice = stat[18].to_i
|
193
|
+
# Skip 19
|
194
|
+
# struct.itrealvalue = stat[20].to_i
|
195
|
+
# struct.starttime = stat[21].to_i
|
196
|
+
# struct.vsize = stat[22].to_i
|
197
|
+
struct.rss = stat[23].to_i
|
198
|
+
# struct.rlim = stat[24].to_i
|
199
|
+
# struct.startcode = stat[25].to_i
|
200
|
+
# struct.endcode = stat[26].to_i
|
201
|
+
# struct.startstack = stat[27].to_i
|
202
|
+
# struct.kstkesp = stat[28].to_i
|
203
|
+
# struct.kstkeip = stat[29].to_i
|
204
|
+
# struct.signal = stat[30].to_i
|
205
|
+
# struct.blocked = stat[31].to_i
|
206
|
+
# struct.sigignore = stat[32].to_i
|
207
|
+
# struct.sigcatch = stat[33].to_i
|
208
|
+
# struct.wchan = stat[34].to_i
|
209
|
+
# struct.nswap = stat[35].to_i
|
210
|
+
# struct.cnswap = stat[36].to_i
|
211
|
+
# struct.exit_signal = stat[37].to_i
|
212
|
+
# struct.processor = stat[38].to_i
|
213
|
+
# struct.rt_priority = stat[39].to_i
|
214
|
+
# struct.policy = stat[40].to_i
|
215
|
+
|
216
|
+
# Get /proc/<pid>/status information (name, uid, euid, gid, egid)
|
217
|
+
# IO.foreach("/proc/#{file}/status") do |line|
|
218
|
+
# case line
|
219
|
+
# when /Name:\s*?(\w+)/
|
220
|
+
# struct.name = $1
|
221
|
+
# when /Uid:\s*?(\d+)\s*?(\d+)/
|
222
|
+
# struct.uid = $1.to_i
|
223
|
+
# struct.euid = $2.to_i
|
224
|
+
# when /Gid:\s*?(\d+)\s*?(\d+)/
|
225
|
+
# struct.gid = $1.to_i
|
226
|
+
# struct.egid = $2.to_i
|
227
|
+
# end
|
228
|
+
# end
|
229
|
+
|
230
|
+
# If cmdline is empty use comm instead
|
231
|
+
# struct.cmdline = struct.comm if struct.cmdline.empty?
|
232
|
+
|
233
|
+
# Manually calculate CPU and memory usage
|
234
|
+
# struct.pctcpu = get_pctcpu(struct.utime, struct.starttime)
|
235
|
+
# struct.pctmem = get_pctmem(struct.rss)
|
236
|
+
|
237
|
+
struct.freeze # This is read-only data
|
238
|
+
|
239
|
+
if block_given?
|
240
|
+
yield struct
|
241
|
+
else
|
242
|
+
array << struct
|
243
|
+
end
|
244
|
+
}
|
245
|
+
|
246
|
+
pid ? struct : array
|
247
|
+
end
|
248
|
+
|
249
|
+
# Returns an array of fields that each ProcTableStruct will contain. This
|
250
|
+
# may be useful if you want to know in advance what fields are available
|
251
|
+
# without having to perform at least one read of the /proc table.
|
252
|
+
#
|
253
|
+
# Example:
|
254
|
+
#
|
255
|
+
# Sys::ProcTable.fields.each{ |field|
|
256
|
+
# puts "Field: #{field}"
|
257
|
+
# }
|
258
|
+
#
|
259
|
+
def self.fields
|
260
|
+
@fields
|
261
|
+
end
|
262
|
+
|
263
|
+
private
|
264
|
+
|
265
|
+
# Calculate the percentage of memory usage for the given process.
|
266
|
+
#
|
267
|
+
def self.get_pctmem(rss)
|
268
|
+
return nil unless @mem_total
|
269
|
+
page_size = 4096
|
270
|
+
rss_total = rss * page_size
|
271
|
+
sprintf("%3.2f", (rss_total.to_f / @mem_total) * 100).to_f
|
272
|
+
end
|
273
|
+
|
274
|
+
# Calculate the percentage of CPU usage for the given process.
|
275
|
+
#
|
276
|
+
def self.get_pctcpu(utime, start_time)
|
277
|
+
return nil unless @boot_time
|
278
|
+
hertz = 100.0
|
279
|
+
utime = (utime * 10000).to_f
|
280
|
+
stime = (start_time.to_f / hertz) + @boot_time
|
281
|
+
sprintf("%3.2f", (utime / 10000.0) / (Time.now.to_i - stime)).to_f
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
data/test/test_basics.rb
CHANGED
@@ -84,7 +84,7 @@ class TestBasics < Test::Unit::TestCase
|
|
84
84
|
p.instance_variable_set '@last_run', last_run
|
85
85
|
p.instance_variable_set '@last_process_list', "bogus value"
|
86
86
|
|
87
|
-
assert_equal({:last_run=>last_run,:last_process_list=>"bogus value"}, p.to_hash)
|
87
|
+
assert_equal({:last_run=>last_run,:last_jiffies=>nil,:last_process_list=>"bogus value"}, p.to_hash)
|
88
88
|
end
|
89
89
|
|
90
90
|
def test_processes_from_hash
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: server_metrics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-01-
|
14
|
+
date: 2014-01-11 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: sys-proctable
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- lib/server_metrics/collectors/memory.rb
|
148
148
|
- lib/server_metrics/collectors/network.rb
|
149
149
|
- lib/server_metrics/collectors/processes.rb
|
150
|
+
- lib/server_metrics/lib/proctable_lite.rb
|
150
151
|
- lib/server_metrics/multi_collector.rb
|
151
152
|
- lib/server_metrics/system_info.rb
|
152
153
|
- lib/server_metrics/version.rb
|
@@ -174,7 +175,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
174
175
|
version: '0'
|
175
176
|
segments:
|
176
177
|
- 0
|
177
|
-
hash:
|
178
|
+
hash: 1552107528440235904
|
178
179
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
180
|
none: false
|
180
181
|
requirements:
|
@@ -183,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
184
|
version: '0'
|
184
185
|
segments:
|
185
186
|
- 0
|
186
|
-
hash:
|
187
|
+
hash: 1552107528440235904
|
187
188
|
requirements: []
|
188
189
|
rubyforge_project:
|
189
190
|
rubygems_version: 1.8.23
|