forked from ManageIQ/manageiq-gems-pending
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmiq-process.rb
267 lines (239 loc) · 10.1 KB
/
miq-process.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# encoding: US-ASCII
require 'sys-uname'
require 'sys/proctable'
require 'util/runcmd'
require 'util/miq-system'
class MiqProcess
def self.get_active_process_by_name(process_name)
pids = []
case Sys::Platform::IMPL
when :mswin, :mingw
require 'util/win32/miq-wmi'
WMIHelper.connectServer.run_query("select Handle,Name from Win32_Process where Name = '#{process_name}.exe'") { |p| pids << p.Handle.to_i }
when :linux, :macosx
pids = `ps -e | grep #{process_name} | grep -v grep `.split("\n").collect(&:to_i)
else
raise "Method MiqProcess.get_active_process_by_name not implemented on this platform [#{Sys::Platform::IMPL}]"
end
pids
end
def self.linux_process_stat(pid = nil)
pid ||= Process.pid
filename = "/proc/#{pid}/stat"
raise Errno::ESRCH.new(pid.to_s) unless File.exist?(filename)
result = {:pid => pid}
raw_stats = MiqSystem.readfile_async(filename)
unless raw_stats.nil?
stats = raw_stats.split(" ")
if pid.to_s == stats.shift
result[:name] = stats.shift.gsub(/(^\(|\)$)/, '')
result[:state] = stats.shift
result[:ppid] = stats.shift.to_i
result[:pgrp] = stats.shift.to_i
result[:session] = stats.shift.to_i
result[:tty_nr] = stats.shift.to_i
result[:tpgid] = stats.shift.to_i
result[:flags] = stats.shift
result[:minflt] = stats.shift.to_i
result[:cminflt] = stats.shift.to_i
result[:majflt] = stats.shift.to_i
result[:cmajflt] = stats.shift.to_i
result[:utime] = stats.shift.to_i
result[:stime] = stats.shift.to_i
result[:cutime] = stats.shift.to_i
result[:cstime] = stats.shift.to_i
result[:priority] = stats.shift.to_i
result[:nice] = stats.shift.to_i
result[:num_threads] = stats.shift.to_i
result[:itrealvalue] = stats.shift.to_i
result[:starttime] = stats.shift.to_i
result[:vsize] = stats.shift.to_i
result[:rss] = stats.shift.to_i
result[:rsslim] = stats.shift.to_i
result[:startcode] = stats.shift.to_i
result[:endcode] = stats.shift.to_i
result[:startstack] = stats.shift.to_i
result[:kstkesp] = stats.shift.to_i
result[:kstkeip] = stats.shift.to_i
result[:signal] = stats.shift
result[:blocked] = stats.shift
result[:sigignore] = stats.shift
result[:sigcatch] = stats.shift
result[:wchan] = stats.shift.to_i
result[:nswap] = stats.shift.to_i
result[:cnswap] = stats.shift.to_i
result[:exit_signal] = stats.shift.to_i
result[:processor] = stats.shift.to_i
result[:rt_priority] = stats.shift.to_i
result[:policy] = stats.shift.to_i
result[:delayacct_blkio_ticks] = stats.shift.to_i
result[:guest_time] = stats.shift.to_i
result[:cguest_time] = stats.shift.to_i
end
end
result
end
def self.processInfo(pid = nil)
pid ||= Process.pid
result = {:pid => pid}
case Sys::Platform::IMPL
when :mswin, :mingw
require 'util/win32/miq-wmi'
# WorkingSetSize: The amount of memory in bytes that a process needs to execute efficiently, for an operating system that uses
# page-based memory management. If an insufficient amount of memory is available (< working set size), thrashing will occur.
# KernelModeTime: Time in kernel mode, in 100 nanoseconds
# UserModeTime : Time in user mode, in 100 nanoseconds.
wmi = WMIHelper.connectServer
process_list_wmi(wmi, pid).each_pair { |_k, v| result = v }
wmi.release
when :linux
x = MiqProcess.linux_process_stat(pid)
result[:name] = x[:name]
result[:priority] = x[:priority]
result[:memory_usage] = x[:rss] * 4096
result[:memory_size] = x[:vsize]
percent_memory = (1.0 * result[:memory_usage]) / MiqSystem.total_memory
result[:percent_memory] = round_to(percent_memory * 100.0, 2)
result[:cpu_time] = x[:stime] + x[:utime]
cpu_status = MiqSystem.status[:cpu]
cpu_total = (0..3).inject(0) { |sum, x| sum + cpu_status[x].to_i }
cpu_total /= MiqSystem.num_cpus
percent_cpu = (1.0 * result[:cpu_time]) / cpu_total
result[:percent_cpu] = round_to(percent_cpu * 100.0, 2)
smaps = Sys::ProcTable.ps(pid).smaps
result[:proportional_set_size] = smaps.pss
result[:unique_set_size] = smaps.uss
when :macosx
h = nil
begin
h = process_list_linux("ps -p #{pid} -o pid,rss,vsize,%mem,%cpu,time,pri,ucomm", true)
rescue
raise Errno::ESRCH.new(pid.to_s)
end
result = h[pid]
end
result
end
def self.command_line(pid)
# Already exited pids, or permission errors cause ps or ps.cmdline to be nil,
# so the best we can do is return an empty string.
Sys::ProcTable.ps(pid).try(:cmdline) || ""
end
def self.alive?(pid)
raise NotImplementedError, "Method MiqProcess.alive? not implemented on this platform [#{Sys::Platform::IMPL}]" unless Sys::Platform::OS == :unix
begin
Process.kill(0, pid)
true
rescue Errno::ESRCH
false
end
end
def self.is_worker?(pid)
command_line = self.command_line(pid)
command_line.include?(MiqWorker::PROCESS_TITLE_PREFIX)
end
def self.process_list_all(wmi = nil)
pl = {}
return process_list_wmi(wmi) unless wmi.nil?
case Sys::Platform::IMPL
when :mswin, :mingw
pl = process_list_wmi(wmi)
when :linux
pl = process_list_linux("ps -e -o pid,rss,vsize,%mem,%cpu,time,priority,ucomm --no-headers")
when :macosx
pl = process_list_linux("ps -e -o pid,rss,vsize,%mem,%cpu,time,pri,ucomm", true)
end
pl
end
def self.process_list_wmi(wmi = nil, pid = nil)
require 'util/win32/miq-wmi'
pl = {}
wmi = WMIHelper.connectServer if wmi.nil?
os_data = wmi.get_instance('select TotalVisibleMemorySize from Win32_OperatingSystem')
proc_query = 'select PageFileUsage,Name,Handle,WorkingSetSize,Priority,UserModeTime,KernelModeTime from Win32_Process'
proc_query += " where Handle = '#{pid}'" unless pid.nil?
proc_data = wmi.run_query(proc_query)
# Calculate the CPU % from a 2 second sampling of the raw perf counters.
perf_query = 'Select IDProcess,PercentProcessorTime,Timestamp_Sys100NS from Win32_PerfRawData_PerfProc_Process'
perf_query += " where IDProcess = '#{pid}'" unless pid.nil?
fh = {}; perf = {}
wmi.run_query(perf_query).each { |p| fh[p.IDProcess] = {:ppt => p.PercentProcessorTime.to_i, :ts => p.Timestamp_Sys100NS.to_i} }
sleep(2)
wmi.run_query(perf_query).each do |p|
m1 = fh[p.IDProcess]
if m1
n = p.PercentProcessorTime.to_i - m1[:ppt]
d = p.Timestamp_Sys100NS.to_i - m1[:ts]
perf[p.IDProcess.to_i] = 100 * n / d
end
end
proc_data.each { |p| next if p.Handle.to_i <= 4; pl[p.Handle.to_i] = parse_process_data(:wmi, p, perf, os_data) }
pl
end
def self.process_list_linux(cmd_str, skip_header = false)
pl, i = {}, 0
rc = MiqUtil.runcmd(cmd_str)
rc.each_line do |ps_str|
i += 1
next if i == 1 && skip_header == true
pinfo = ps_str.strip.split(' ')
nh = parse_process_data(:linux, pinfo, perf = nil, os = nil)
pl[nh[:pid]] = nh
pl
end
pl
end
def self.parse_process_data(data_type, pinfo, perf = nil, os = nil)
nh = {}
if data_type == :wmi
nh[:pid] = pinfo.Handle.to_i
nh[:name] = pinfo.Name
nh[:memory_size] = pinfo.WorkingSetSize.to_i
nh[:memory_usage] = nh[:memory_size] - pinfo.PageFileUsage.to_i * 1024
# Keep the percent format to 2 decimal places
nh[:percent_memory] = sprintf("%.2f", pinfo.WorkingSetSize.to_f / (os.TotalVisibleMemorySize.to_i * 1024) * 100)
nh[:cpu_time] = (pinfo.UserModeTime.to_i + pinfo.KernelModeTime.to_i) / 10000000 # in seconds
nh[:priority] = pinfo.Priority.to_i
nh[:percent_cpu] = perf[nh[:pid]]
else
nh[:pid] = pinfo[0].to_i
nh[:memory_usage] = pinfo[1].to_i * 1024 # Memory in RAM
nh[:memory_size] = pinfo[2].to_i * 1024 # Memory in RAM and swap
nh[:percent_memory] = pinfo[3]
nh[:percent_cpu] = pinfo[4]
nh[:cpu_time] = str_time_to_sec(pinfo[5])
nh[:priority] = pinfo[6]
nh[:name] = pinfo[7..-1].join(' ')
end
nh
end
def self.str_time_to_sec(time_str)
# Convert format 00:00:00 to seconds
t = time_str.split(':')
(t[0].to_i * 3600) + (t[1].to_i * 60) + t[2].to_i
end
def self.suspend_process(pid)
case Sys::Platform::OS
when :windows then Process.process_thread_list[pid].each { |tid| Process.suspend_resume_thread(tid, false) }
else
raise "Method MiqProcess.suspend_process not implemented on this platform [#{Sys::Platform::IMPL}]"
end
end
def self.resume_process(pid)
case Sys::Platform::OS
when :windows then Process.process_thread_list[pid].each { |tid| Process.suspend_resume_thread(tid, true) }
else
raise "Method MiqProcess.resume_process not implemented on this platform [#{Sys::Platform::IMPL}]"
end
end
def self.round_to(number, precision)
mult = 10**precision
(number * mult).round.to_f / mult
end
end
# Examples:
# puts MiqProcess.processInfo().inspect
# puts MiqProcess.process_list_all().inspect
# MiqProcess.process_list_all().each_pair do |k,v|
# puts v.inspect
# end